﻿// 麻省理工学院许可证
//
// 版权所有 (c) 2021-2023 zuohuaijun，大名科技（天津）有限公司  联系电话/微信：18020030720  QQ：515096995
//
// 特此免费授予获得本软件的任何人以处理本软件的权利，但须遵守以下条件：在所有副本或重要部分的软件中必须包括上述版权声明和本许可声明。
//
// 软件按“原样”提供，不提供任何形式的明示或暗示的保证，包括但不限于对适销性、适用性和非侵权的保证。
// 在任何情况下，作者或版权持有人均不对任何索赔、损害或其他责任负责，无论是因合同、侵权或其他方式引起的，与软件或其使用或其他交易有关。
using Admin.NET.Core.Service;
using Furion.Extras.Admin.NET.Util;
using Microsoft.AspNetCore.Authorization;
using Newtonsoft.Json;
using Nito.AsyncEx;

namespace Admin.NET.Application.Service.EG_WMS_BaseServer;

/// <summary>
/// 基础实用接口
/// </summary>
[ApiDescriptionSettings(ApplicationConst.GroupName, Order = 100)]
public class BaseService : IDynamicApiController, ITransient
{
    private static readonly AsyncLock _mutex = new AsyncLock();
    private static readonly System.Net.Http.HttpClient _HttpClient = new System.Net.Http.HttpClient();

    #region 关系注入
    EG_WMS_InAndOutBoundMessage boundMessage = new EG_WMS_InAndOutBoundMessage();
    private readonly SqlSugarRepository<TaskEntity> _TaskEntity = App.GetService<SqlSugarRepository<TaskEntity>>();
    private readonly SqlSugarRepository<TaskDetailEntity> _TaskDetailEntity = App.GetService<SqlSugarRepository<TaskDetailEntity>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_InAndOutBound> _InAndOutBound = App.GetService<SqlSugarRepository<Entity.EG_WMS_InAndOutBound>>();
    private readonly SqlSugarRepository<EG_WMS_Tem_Inventory> _TemInventory = App.GetService<SqlSugarRepository<EG_WMS_Tem_Inventory>>();
    private readonly SqlSugarRepository<EG_WMS_Tem_InventoryDetail> _TemInventoryDetail = App.GetService<SqlSugarRepository<EG_WMS_Tem_InventoryDetail>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_Inventory> _Inventory = App.GetService<SqlSugarRepository<Entity.EG_WMS_Inventory>>();
    private readonly SqlSugarRepository<EG_WMS_InAndOutBoundDetail> _InAndOutBoundDetail = App.GetService<SqlSugarRepository<EG_WMS_InAndOutBoundDetail>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_Region> _Region = App.GetService<SqlSugarRepository<Entity.EG_WMS_Region>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_Storage> _Storage = App.GetService<SqlSugarRepository<Entity.EG_WMS_Storage>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_WorkBin> _WorkBin = App.GetService<SqlSugarRepository<Entity.EG_WMS_WorkBin>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_Materiel> _Materiel = App.GetService<SqlSugarRepository<Entity.EG_WMS_Materiel>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_WareHouse> _WHouse = App.GetService<SqlSugarRepository<Entity.EG_WMS_WareHouse>>();
    private readonly SqlSugarRepository<Entity.EG_WMS_MaterielLevel> _MateielLevel = App.GetService<SqlSugarRepository<Entity.EG_WMS_MaterielLevel>>();
    private SysCacheService _cacheServices = App.GetRequiredService<SysCacheService>();

    //private readonly ILoggerFactory //_loggerFactory;
    //private readonly ILogger //_logger;
    #endregion

    #region 构造函数
    public BaseService()
    {
        ////_loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
        //{
        //    builder.AddFile("logs/BaseStrategy-{0:yyyy}{0:MM}{0:dd}.log", options =>
        //    {
        //        options.FileNameRule = fileName =>
        //        {
        //            return string.Format(fileName, DateTime.Now);
        //        };
        //        //options.MessageFormat = logmsg =>
        //        //{
        //        //    //messagelogData //messagelogData = new //messagelogData();
        //        //    //messagelogData.StorageGroup = logmsg.Context.Get("StorageGroup").ToString();
        //        //    return logmsg.Context.Get("StorageGroup").ToString();
        //        //};
        //    });
        //});
        ////_logger = //_loggerFactory.CreateLogger("Wms");
    }
    #endregion

    ToolTheCurrentTime toolTimes = new ToolTheCurrentTime();

    #region 得到所有的库存信息以及关联关系

    ///// <summary>
    ///// 得到所有的库存信息以及关联关系（分页查询）
    ///// 修改
    ///// </summary>
    ///// <param name="page">分页页数</param>
    ///// <param name="pageSize">每页容量</param>
    ///// <returns></returns>

    //[HttpPost]
    //[ApiDescriptionSettings(Name = "GetAllInventoryMessage")]
    //public async Task<SqlSugarPagedList<GetAllInventoryData>> GetAllInventoryMessage(int page, int pageSize = 100)
    //{

    //    //var data = _Storage.AsQueryable()
    //    //        .LeftJoin<EG_WMS_InventoryDetail>((a, b) => a.StorageNum == b.StorageNum)
    //    //        .LeftJoin<Entity.EG_WMS_Inventory>((a, b, c) => b.InventoryNum == c.InventoryNum)
    //    //        .OrderBy((a, b, c) => a.StorageNum, OrderByType.Asc)
    //    //        .GroupBy((a, b, c) => a.StorageNum)
    //    //        .Where((a, b, c) => c.OutboundStatus == 0)
    //    //        .Select((a, b, c) => new GetAllInventoryData
    //    //        {
    //    //            StorageNum = a.StorageNum,
    //    //            MaterielNum = SqlFunc.IsNull(c.MaterielNum, null),
    //    //            ProductionLot = SqlFunc.IsNull(b.ProductionLot, null),
    //    //            ICountAll = SqlFunc.IsNull(SqlFunc.Subqueryable<Entity.EG_WMS_Inventory>().Where(x => x.OutboundStatus == 0).Select(x => SqlFunc.AggregateSum(x.ICountAll)), 0),
    //    //            StorageTime = SqlFunc.IsNull(c.CreateTime, null)
    //    //        });

    //    return await data.ToPagedListAsync(page, pageSize);

    //}

    #endregion

    #region 查询得到出入库详情表

    /// <summary>
    /// 查询得到出入库详情表
    /// </summary>
    /// <param name="inandoutbound">出入库编号</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "GetAllInAndBoundDetailMessage")]
    public GetAllInAndBoundDetailData GetAllInAndBoundDetailMessage(string inandoutbound)
    {

        string inandoutboundnum = "";
        if (inandoutbound.Contains("EGTMRK") || inandoutbound.Contains("EGRK") || inandoutbound.Contains("EGEFRK"))
        {
            inandoutboundnum = inandoutbound;
            var inbounddata = _InAndOutBound.AsQueryable()
                           .InnerJoin<EG_WMS_InAndOutBoundDetail>((outinbound, outinbounddetail) => outinbound.InAndOutBoundNum == outinbounddetail.InAndOutBoundNum)
                           .InnerJoin<Entity.EG_WMS_Materiel>((outinbound, outinbounddetail, materiel) => materiel.MaterielNum == outinbounddetail.MaterielNum)
                           .Where((outinbound, outinbounddetail, materiel) => outinbound.InAndOutBoundNum == inandoutboundnum && outinbound.InAndOutBoundStatus == 1 && outinbound.SuccessOrNot == 0)
                           .Select((outinbound, outinbounddetail, materiel) => new
                           {
                               materiel.MaterielNum,
                               materiel.MaterielName,
                               outinbound.InAndOutBoundTime,
                           })
                           .First();

            var materieldata = _Inventory.AsQueryable()
                       .InnerJoin<EG_WMS_InventoryDetail>((inv, invd) => inv.InventoryNum == invd.InventoryNum)
                       .Where((inv, invd) => inv.OutboundStatus == 0 && inv.InBoundNum == inandoutboundnum)
                       .Select((inv, invd) => new MaterielProductDto
                       {
                           MaterielNum = inv.MaterielNum,
                           ProductCount = (int)inv.ICountAll,
                           ProductionDate = (DateTime)inv.ProductionDate,
                           ProductionLot = invd.ProductionLot,
                       })
                       .ToList();

            GetAllInAndBoundDetailData data = new GetAllInAndBoundDetailData();
            data.MaterielNum = inbounddata.MaterielNum;
            data.MaterielName = inbounddata.MaterielName;
            data.InAndOutBoundTime = (DateTime)inbounddata.InAndOutBoundTime;
            data.ProductData = materieldata ?? null;

            return data;
        }
        else if (inandoutbound.Contains("EGTMCK") || inandoutbound.Contains("EGCK") || inandoutbound.Contains("EGEFCK"))
        {
            inandoutboundnum = inandoutbound;
            var inbounddata = _InAndOutBound.AsQueryable()
                           .InnerJoin<EG_WMS_InAndOutBoundDetail>((outinbound, outinbounddetail) => outinbound.InAndOutBoundNum == outinbounddetail.InAndOutBoundNum)
                           .InnerJoin<Entity.EG_WMS_Materiel>((outinbound, outinbounddetail, materiel) => materiel.MaterielNum == outinbounddetail.MaterielNum)
                           .Where((outinbound, outinbounddetail, materiel) => outinbound.InAndOutBoundNum == inandoutboundnum && outinbound.InAndOutBoundStatus == 3 && outinbound.SuccessOrNot == 0)
                           .Select((outinbound, outinbounddetail, materiel) => new
                           {
                               materiel.MaterielNum,
                               materiel.MaterielName,
                               outinbound.InAndOutBoundTime,
                           })
                           .First();

            var materieldata = _Inventory.AsQueryable()
                       .InnerJoin<EG_WMS_InventoryDetail>((inv, invd) => inv.InventoryNum == invd.InventoryNum)
                       .Where((inv, invd) => inv.OutboundStatus == 1 && inv.OutBoundNum == inandoutboundnum)
                       .Select((inv, invd) => new MaterielProductDto
                       {
                           MaterielNum = inv.MaterielNum,
                           ProductCount = (int)inv.ICountAll,
                           ProductionDate = (DateTime)inv.ProductionDate,
                           ProductionLot = invd.ProductionLot,
                       })
                       .ToList();

            GetAllInAndBoundDetailData data = new GetAllInAndBoundDetailData();
            data.MaterielNum = inbounddata.MaterielNum;
            data.MaterielName = inbounddata.MaterielName;
            data.InAndOutBoundTime = (DateTime)inbounddata.InAndOutBoundTime;
            data.ProductData = materieldata ?? null;

            return data;
        }
        return null;
    }

    #endregion

    #region 根据物料筛选条件查询物料的总数

    /// <summary>
    /// 根据物料筛选条件查询物料的总数
    /// </summary>
    /// <param name="input">查询条件</param>
    /// <returns></returns>

    [HttpPost]
    [ApiDescriptionSettings(Name = "MaterialAccorDingSumCount")]
    public async Task<SqlSugarPagedList<MaterielDataSumDto>> MaterialAccorDingSumCount(MaterialSelectSumCountBO input)
    {
        var data = _Inventory.AsQueryable()
                   .InnerJoin<Entity.EG_WMS_Materiel>((inv, mat) => inv.MaterielNum == mat.MaterielNum)
                   .WhereIF(!string.IsNullOrEmpty(input.materielNum), (inv, mat) => mat.MaterielNum == input.materielNum)
                   .WhereIF(!string.IsNullOrEmpty(input.materielName), (inv, mat) => mat.MaterielName.Contains(input.materielName.Trim()))
                   .WhereIF(!string.IsNullOrEmpty(input.materielType), (inv, mat) => mat.MaterielType.Contains(input.materielType.Trim()))
                   .WhereIF(!string.IsNullOrEmpty(input.materielSpecs), (inv, mat) => mat.MaterielSpecs.Contains(input.materielSpecs.Trim()))
                   .Where((inv, mat) => inv.OutboundStatus == 0 && inv.IsDelete == false)
                   .GroupBy((inv, mat) => inv.MaterielNum)
                   .Select((inv, mat) => new MaterielDataSumDto
                   {
                       MaterielNum = inv.MaterielNum,
                       MaterielName = mat.MaterielName,
                       MaterielType = mat.MaterielType,
                       MaterielSpecs = mat.MaterielSpecs,
                       MaterielMainUnit = mat.MaterielMainUnit,
                       MaterielAssistUnit = mat.MaterielAssistUnit,
                       SumCount = (int)SqlFunc.AggregateSum(inv.ICountAll)
                   });

        return await data.ToPagedListAsync(input.page, input.pageSize);

    }
    #endregion 

    #region 根据物料编号得到这条物料编号所有的库存记录

    /// <summary>
    /// 根据物料编号得到这条物料编号所有的库存记录
    /// </summary>
    /// <param name="MaterielNum">物料编号</param>
    /// <param name="page">页数</param>
    /// <param name="pageSize">每页容量</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "GetMaterileNumAllInventoryRecords")]
    public async Task<SqlSugarPagedList<GetMaterielNumDataList>> GetMaterileNumAllInventoryRecords(string MaterielNum, int page, int pageSize)
    {
        var data = _Inventory.AsQueryable()
                   .InnerJoin<EG_WMS_InventoryDetail>((inv, invd) => inv.InventoryNum == invd.InventoryNum)
                   .InnerJoin<Entity.EG_WMS_Materiel>((inv, invd, ma) => inv.MaterielNum == ma.MaterielNum)
                   .InnerJoin<Entity.EG_WMS_Region>((inv, invd, ma, re) => invd.RegionNum == re.RegionNum)
                   .InnerJoin<Entity.EG_WMS_WareHouse>((inv, invd, ma, re, wh) => invd.WHNum == wh.WHNum)
                   .Where((inv, invd) => inv.MaterielNum == MaterielNum && inv.OutboundStatus == 0 && inv.IsDelete == false)
                   .Select<GetMaterielNumDataList>();

        return await data.ToPagedListAsync(page, pageSize);

    }



    #endregion

    #region 展示有库存的库位信息

    /// <summary>
    /// 展示有库存的库位信息
    /// </summary>
    /// <param name="page">页数</param>
    /// <param name="pageSize">每页容量</param>
    /// <returns></returns>
    [ApiDescriptionSettings(Name = "StorageExistInventory"), HttpPost]
    public async Task<SqlSugarPagedList<SelectExistInventoryDto>> StorageExistInventory(int page, int pageSize)
    {
        var data = _Storage.AsQueryable()
                                .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                                .Select(x => new SelectExistInventoryDto
                                {
                                    StorageNum = x.StorageNum,
                                    StorageName = x.StorageName,
                                    ShelfNum = (int)x.ShelfNum,
                                    RoadwayNum = (int)x.RoadwayNum,
                                    FloorNumber = (int)x.FloorNumber,
                                });

        return await data.ToPagedListAsync(page, pageSize);

    }

    #endregion

    #region 根据库存信息，物料数据在哪几个立库库位上

    /// <summary>
    /// 根据库存信息，物料数据在哪几个立库库位上
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <param name="page">页码</param>
    /// <param name="pageSize">每页容量</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AccordingTheInventorySetUpStorageNum")]
    public async Task<SqlSugarPagedList<AccorDingSetUpStorageDto>> AccordingTheInventorySetUpStorageNum(string materielNum, int page, int pageSize)
    {
        var data = await _Inventory.AsQueryable()
                   .InnerJoin<EG_WMS_InventoryDetail>((a, b) => a.InventoryNum == b.InventoryNum)
                   .InnerJoin<Entity.EG_WMS_Storage>((a, b, c) => b.StorageNum == c.StorageNum)
                   .Where((a, b, c) => a.MaterielNum == materielNum && a.OutboundStatus == 0 && c.StorageType == 1)
                   .Select((a, b, c) => new AccorDingSetUpStorageDto
                   {
                       StorageNum = b.StorageNum,
                       StorageName = c.StorageName,
                       MaterielNum = a.MaterielNum,
                       CountSum = (int)a.ICountAll,

                   })
                   .ToListAsync();

        return data.ToPagedListAsync(page, pageSize);

    }


    #endregion

    #region 每月的入库数量

    /// <summary>
    /// 每月的入库数量
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "MonthlyInventoryQuantity")]
    public List<MonthlyInventoryQuantityDto> MonthlyInventoryQuantity()
    {
        //SELECT DATE_FORMAT(InAndOutBoundTime,'%Y-%m') AS 月份,SUM(InAndOutBoundCount) AS 总数 FROM eg_wms_inandoutbound
        //WHERE InAndOutBoundType = 0 AND InAndOutBoundStatus = 1
        //GROUP BY DATE_FORMAT(InAndOutBoundTime, '%Y-%m')

        return _InAndOutBound.AsQueryable()
                     .Where(x => x.InAndOutBoundStatus == 1 && x.InAndOutBoundType == 0 && x.SuccessOrNot == 0)
                     .GroupBy(x => SqlFunc.MappingColumn(x.InAndOutBoundTime.Value.ToString(), "DATE_FORMAT(InAndOutBoundTime, '%Y-%m')"))
                     .Select(x => new MonthlyInventoryQuantityDto
                     {
                         Mouth = SqlFunc.MappingColumn(x.InAndOutBoundTime.Value.ToString(), "DATE_FORMAT(InAndOutBoundTime, '%Y-%m')"),
                         CountSum = (int)SqlFunc.AggregateSum(x.InAndOutBoundCount)
                     })
                     .ToList();

    }


    #endregion

    #region 每月的出库数量

    /// <summary>
    /// 每月的出库数量
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    [ApiDescriptionSettings(Name = "MonthlyOutboundQuantity")]
    public List<MonthlyInventoryQuantityDto> MonthlyOutboundQuantity()
    {

        return _InAndOutBound.AsQueryable()
             .Where(x => x.InAndOutBoundStatus == 3 && x.InAndOutBoundType == 1 && x.SuccessOrNot == 0)
             .GroupBy(x => SqlFunc.MappingColumn(x.InAndOutBoundTime.Value.ToString(), "DATE_FORMAT(InAndOutBoundTime, '%Y-%m')"))
             .Select(x => new MonthlyInventoryQuantityDto
             {
                 Mouth = SqlFunc.MappingColumn(x.InAndOutBoundTime.Value.ToString(), "DATE_FORMAT(InAndOutBoundTime, '%Y-%m')"),
                 CountSum = (int)SqlFunc.AggregateSum(x.InAndOutBoundCount)
             })
             .ToList();

    }


    #endregion

    #region 所有已占用库位数量

    /// <summary>
    /// 所有已占用库位数量
    /// </summary>
    /// <returns></returns>
    public int GetAllOccupiedStorage()
    {
        return _Storage.AsQueryable()
                       .Where(x => x.StorageOccupy == 1 && x.IsDelete == false && x.StorageStatus == 0)
                       .Count();
    }

    #endregion

    #region 所有未占用库位数量

    /// <summary>
    /// 所有未占用库位数量
    /// </summary>
    /// <returns></returns>
    public int GetAllUnoccupiedStorage()
    {
        return _Storage.AsQueryable()
                       .Where(x => x.StorageOccupy == 0 && x.IsDelete == false && x.StorageStatus == 0)
                       .Count();
    }

    #endregion

    #region 根据在库库存查询物料

    /// <summary>
    /// 根据在库库存查询物料
    /// </summary>
    /// <returns></returns>

    [HttpPost]
    [ApiDescriptionSettings(Name = "QueryMaterialsAccordingInventory")]
    public async Task<SqlSugarPagedList<AccordingInventoryDto>> QueryMaterialsAccordingInventory(QueryMaterielAccordingBO model)
    {
        return await _Inventory.AsQueryable()
           .InnerJoin<Entity.EG_WMS_Materiel>((a, b) => a.MaterielNum == b.MaterielNum)
           .Where(a => a.OutboundStatus == 0 && a.IsDelete == false)
           .WhereIF(!string.IsNullOrEmpty(model.MaterielNum), (a, b) => a.MaterielNum.Contains(model.MaterielNum))
           .WhereIF(!string.IsNullOrEmpty(model.MaterielSpecs), (a, b) => b.MaterielSpecs.Contains(model.MaterielSpecs))
           .Distinct()
           .Select((a, b) => new AccordingInventoryDto
           {
               MaterielNum = a.MaterielNum,
               MaterielName = b.MaterielName,
               MaterielType = b.MaterielType,
               MaterielMainUnit = b.MaterielMainUnit,
               MaterielSpecs = b.MaterielSpecs,
           })
           .ToPagedListAsync(model.page, model.pageSize);

    }

    #endregion

    //-------------------------------------/策略/-------------------------------------//

    #region （策略）（密集库）AGV入库WMS自动推荐的库位（优先最靠里的库位）《无缓存》

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位《无缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommEndStorage", Order = 1000)]
    public string AGVStrategyReturnRecommEndStorage(string materielNum)
    {
        //using (await _mutex.LockAsync())
        //{
        //Console.WriteLine("开始执行");
        //await Task.Delay(3000);
        // 根据物料编号，得到这个物料属于那个区域
        // 排除尾料区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).Where(x => !x.RegionNum.Contains("RegionTM")).ToList();

        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }

        for (int k = 0; k < dataRegion.Count; k++)
        {

            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                Console.WriteLine("结束");
                return data[0].StorageNum.ToString();
            }

            #endregion


            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                     .OrderBy(a => a.StorageNum, OrderByType.Desc)
                     .Select(a => new
                     {
                         a.StorageNum,
                         a.StorageGroup,
                     })
                     .ToList();


            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();


            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位

                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                // 如果当前组没有占用的库位

                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();
                    Console.WriteLine("结束");
                    return datalist[0].StorageNum;
                }

                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            Console.WriteLine("结束");
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
                else
                {
                    //throw Oops.Oh("没有合适的库位");
                    return "没有合适的库位";

                }
            }
        }

        // 如果没有则返回错误
        return "没有合适的库位";

        //throw Oops.Oh("没有合适的库位");
        //}
    }

    #endregion

    #region （策略）（密集库）AGV入库WMS自动推荐的库位（优先最靠里的库位）《有缓存》

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位《有缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVCacheStrategyReturnRecommEndStorage", Order = 1000)]
    public string AGVCacheStrategyReturnRecommEndStorage(string materielNum)
    {
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }
        // string //messagelog = "";
        #region 缓存组别
        // 缓存集合
        List<string> cachelist = _cacheServices.GetKeyList();
        IEnumerable<string> materielcache = cachelist.Where(x => { return x.Contains("Mat_InBoundGroup_" + materielNum); });
        // 缓存key
        string cachekey = "";
        // 没有搜索到这个物料的缓存
        if (!materielcache.Any())
        {
            // 新建缓存
            cachekey = $"Mat_InBoundGroup_{materielNum}";
            ////messagelog = $"①新建了缓存，key：{cachekey}";
            ////_logger.LogInformation(//messagelog);
            _cacheServices.Set(cachekey, null);
        }
        else
        {
            // 查询缓存里的数据
            object cachematerielvalue = _cacheServices.GetValue(materielcache.FirstOrDefault());
            cachekey = materielcache.FirstOrDefault();
            ////messagelog = $"②查到了缓存数据，key：{cachekey}，value：{cachematerielvalue.ToString()}";
            ////_logger.LogInformation(//messagelog);
            // 缓存里面有数据
            if (cachematerielvalue is string)
            {
                var beoccupiedstorage = _Storage.AsQueryable()
                .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                .Where(x => x.StorageStatus == 0)
                .Where(x => x.StorageOccupy == 2)
                .ToList();
                // 这列有任务
                if (beoccupiedstorage.Count != 0)
                {
                    ////messagelog = $"③有任务等待任务完成";
                    ////_logger.LogInformation(//messagelog);
                    return "等待任务完成！";
                }

                // 查询得到当前组别下有占用的库位
                var allStorageOccupy = _Storage.AsQueryable()
                         .Where(x => x.StorageOccupy == 1 && x.StorageGroup == cachematerielvalue.ToString())
                         .Where(x => x.StorageStatus == 0)
                         .OrderBy(a => a.StorageNum, OrderByType.Desc)
                         .Select(a => new
                         {
                             a.StorageNum,
                             a.StorageGroup,
                         })
                         .ToList();

                ////messagelog = $"④缓存组别下有占用的库位:{string.Join(",", allStorageOccupy.Select(x => x.StorageNum.ToString()))}";
                ////_logger.LogInformation(//messagelog);
                // 这组没有被占用的库位
                if (allStorageOccupy.Count == 0)
                {
                    var InBoundStorage = _Storage.AsQueryable()
                              .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                              .Where(x => x.StorageStatus == 0)
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(x => new
                              {
                                  x.StorageNum,
                                  x.StorageGroup
                              })
                              .ToList();

                    // 将缓存保存
                    _cacheServices.Set(cachekey, cachematerielvalue.ToString());
                    ////messagelog = $"⑤通过缓存推荐库位，组别为：{cachematerielvalue.ToString()}，推荐的库位为：{InBoundStorage.First().StorageNum.ToString()}";
                    ////_logger.LogInformation(//messagelog);
                    return InBoundStorage.First().StorageNum.ToString();
                }

                // TODO:
                // 调用其他接口排除能出库的
                // 如果能出库的组别在缓存中，则需要重新筛选缓存
                //List<string> canoutboundlist = GetOutBoundStorageGroup(materielNum);
                //if (canoutboundlist.Contains(cachematerielvalue.ToString()))
                //{
                //    _cacheServices.Set(cachekey, null);
                //    goto getgroup;
                //}

                // 得到这个组别下所有的未占用库位
                var GetGroupOccupyNot = _Storage.AsQueryable()
                              .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                     x.StorageGroup == cachematerielvalue.ToString())
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(it => new
                              {
                                  it.StorageNum,
                                  it.StorageGroup
                              })
                              .ToList();

                // //messagelog = $"⑥当前组别下未占用的库位：{string.Join(",", GetGroupOccupyNot.Select(x => x.StorageNum.ToString()))}";
                // //_logger.LogInformation(//messagelog);
                // 这组已经没有库位可以用
                if (GetGroupOccupyNot.Count == 0)
                {
                    // 将缓存保存
                    _cacheServices.Set(cachekey, null);
                    goto getgroup;
                }

                // 依次判断符合条件的数据
                // 当前组别最后一个被占用的库位编号
                int lastOccupyNum = allStorageOccupy.Last().StorageNum.ToInt();

                for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                {
                    if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                    {
                        // 将缓存保存
                        _cacheServices.Set(cachekey, cachematerielvalue.ToString());
                        // //messagelog = $"⑥缓存推荐库位，组别为：{cachematerielvalue.ToString()}，当前组别最后一个被占用的库位：{lastOccupyNum}，推荐的库位为：{GetGroupOccupyNot[j].StorageNum}";
                        // //_logger.LogInformation(//messagelog);
                        return GetGroupOccupyNot[j].StorageNum;
                    }
                }
            }
        }
    #endregion

    getgroup:
        for (int k = 0; k < dataRegion.Count; k++)
        {

            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum,
                         x.StorageGroup
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                // 将缓存保存
                _cacheServices.Set(cachekey, data[0].StorageGroup);
                //  //messagelog = $"⑦推荐的库位:{data[0].StorageNum}";
                // //_logger.LogInformation(//messagelog);
                return data[0].StorageNum.ToString();
            }

            #endregion

            // 查询是否有正在进行中的任务库位的组别
            var dataStorageGroup = _Storage.AsQueryable()
                        .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                        .Distinct()
                        .Select(a => new
                        {
                            a.StorageGroup,
                        })
                        .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // //messagelog = $"⑧当前推荐库位时，有任务的组别：{string.Join(",", dataStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            // //_logger.LogInformation(//messagelog);
            // 查询库位并且排除不符合条件的组别和库位
            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                     .OrderBy(a => a.StorageNum, OrderByType.Desc)
                     .Select(a => new
                     {
                         a.StorageNum,
                         a.StorageGroup,
                     })
                     .ToList();

            // //messagelog = $"⑨查询组别排除不符合条件的组别：{string.Join(",", getStorage.Select(x => x.StorageGroup.ToString()))}";
            ////_logger.LogInformation(//messagelog);
            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();

            //    //messagelog = $"⑩得到所有合适的组别：{string.Join(",", getStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            // //_logger.LogInformation(//messagelog);
            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位
                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                // 如果当前组没有占用的库位
                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                    // 将缓存保存
                    _cacheServices.Set(cachekey, getStorageGroup[i].StorageGroup);
                    //     //messagelog = $"⑪通过策略推荐库位，组别为：{getStorageGroup[i].StorageGroup}，推荐的库位为：{datalist[0].StorageNum}";
                    //     //_logger.LogInformation(//messagelog);
                    return datalist[0].StorageNum;
                }

                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            // 将缓存保存
                            _cacheServices.Set(cachekey, allStorageOccupy.StorageGroup);
                            //   //messagelog = $"⑫通过策略推荐库位，组别为：{allStorageOccupy.StorageGroup}，本组下最后一个被占用的库位为：{lastOccupyNum}，推荐的库位为：{GetGroupOccupyNot[j].StorageNum}";
                            //   //_logger.LogInformation(//messagelog);
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
                else
                {
                    //  //messagelog = $"⑬没有合适的库位";
                    //  //_logger.LogInformation(//messagelog);
                    return "没有合适的库位";
                }
            }
        }
        //   //messagelog = $"⑬没有合适的库位";
        // //_logger.LogInformation(//messagelog);
        return "没有合适的库位";
    }

    #endregion

    #region （策略）（密集库）AGV入库WMS自动推荐的库位（排除能出库的组别）

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位（排除能出库的组别）
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommEndStorageExcludeOutBoundStorage", Order = 1000)]
    public string AGVStrategyReturnRecommEndStorageExcludeOutBoundStorage(string materielNum)
    {
        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        List<string> outBoundGroup = GetOutBoundStorageGroup(materielNum);
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }

        for (int k = 0; k < dataRegion.Count; k++)
        {

            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                return data[0].StorageNum.ToString();
            }

            #endregion


            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 // 排除可以出库的组别
                                 .Where(a => !outBoundGroup.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();


            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位

                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                // 如果当前组没有占用的库位

                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                    return datalist[0].StorageNum;
                }

                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
                else
                {
                    //throw Oops.Oh("没有合适的库位");
                    return "没有合适的库位";

                }
            }
        }

        // 如果没有则返回错误
        return "没有合适的库位";

        //throw Oops.Oh("没有合适的库位");
    }

    #endregion

    #region （策略）（密集库）AGV入库WMS自动推荐的库位（已修改）《有缓存》

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位（已修改）《有缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage", Order = 1000)]
    public string AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage(string materielNum)
    {
        // 根据物料编号，得到这个物料属于那个区域
        // 排除尾料区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).Where(x => !x.RegionNum.Contains("RegionTM")).ToList();
        List<string> outBoundGroup = GetOutBoundStorageGroup(materielNum);
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }
        //string //messagelog = "";
        #region 缓存组别
        // 缓存集合
        List<string> cachelist = _cacheServices.GetKeyList();
        IEnumerable<string> materielcache = cachelist.Where(x => { return x.Contains("Mat_InBoundGroup_" + materielNum); });
        // 缓存key
        string cachekey = "";
        // 没有搜索到这个物料的缓存
        if (!materielcache.Any())
        {
            // 新建缓存
            cachekey = $"Mat_InBoundGroup_{materielNum}";
            ////messagelog = $"①新建了缓存，key：{cachekey}";
            // //_logger.LogInformation(//messagelog);
            _cacheServices.Set(cachekey, null);
        }
        else
        {
            // 查询缓存里的数据
            object cachematerielvalue = _cacheServices.GetValue(materielcache.FirstOrDefault());
            cachekey = materielcache.FirstOrDefault();
            ////messagelog = $"②查到了缓存数据，key：{cachekey}，value：{cachematerielvalue.ToString()}";
            ////_logger.LogInformation(//messagelog);
            // 缓存里面有数据
            if (cachematerielvalue is string)
            {
                var beoccupiedstorage = _Storage.AsQueryable()
                .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                .Where(x => x.StorageStatus == 0)
                .Where(x => x.StorageOccupy == 2)
                .ToList();
                // 这列有任务
                if (beoccupiedstorage.Count != 0)
                {
                    ////messagelog = $"③有任务等待任务完成";
                    ////_logger.LogInformation(//messagelog);
                    return "等待任务完成！";
                }

                // 查询得到当前组别下有占用的库位
                var allStorageOccupy = _Storage.AsQueryable()
                         .Where(x => x.StorageOccupy == 1 && x.StorageGroup == cachematerielvalue.ToString())
                         .Where(x => x.StorageStatus == 0)
                         .OrderBy(a => a.StorageNum, OrderByType.Desc)
                         .Select(a => new
                         {
                             a.StorageNum,
                             a.StorageGroup,
                         })
                         .ToList();

                ////messagelog = $"④缓存组别下有占用的库位:{string.Join(",", allStorageOccupy.Select(x => x.StorageNum.ToString()))}";
                ////_logger.LogInformation(//messagelog);
                // 这组没有被占用的库位
                if (allStorageOccupy.Count == 0)
                {
                    var InBoundStorage = _Storage.AsQueryable()
                              .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                              .Where(x => x.StorageStatus == 0)
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(x => new
                              {
                                  x.StorageNum,
                                  x.StorageGroup
                              })
                              .ToList();

                    // 将缓存保存
                    _cacheServices.Set(cachekey, cachematerielvalue.ToString());

                    ////messagelog = $"⑤通过缓存推荐库位,本组所有的库位:{string.Join(",", InBoundStorage.Select(x => x.StorageNum.ToString()))}";
                    ////_logger.LogInformation(//messagelog);
                    return InBoundStorage.First().StorageNum.ToString();
                }

                // 得到这个组别下所有的未占用库位
                var GetGroupOccupyNot = _Storage.AsQueryable()
                              .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                     x.StorageGroup == cachematerielvalue.ToString())
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(it => new
                              {
                                  it.StorageNum,
                                  it.StorageGroup
                              })
                              .ToList();

                ////messagelog = $"⑥缓存组别下所有未占用的库位:{string.Join(",", GetGroupOccupyNot.Select(x => x.StorageNum.ToString()))}";
                ////_logger.LogInformation(//messagelog);

                // 这组已经没有库位可以用
                if (GetGroupOccupyNot.Count == 0)
                {
                    // 将缓存保存
                    _cacheServices.Set(cachekey, null);
                    ////messagelog = $"⑦缓存组别下已没有库位";
                    ////_logger.LogInformation(//messagelog);
                    goto getgroup;
                }

                // 依次判断符合条件的数据
                // 当前组别最后一个被占用的库位编号
                int lastOccupyNum = allStorageOccupy.Last().StorageNum.ToInt();

                for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                {
                    if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                    {
                        // 将缓存保存
                        _cacheServices.Set(cachekey, GetGroupOccupyNot[0].StorageGroup);
                        ////messagelog = $"⑧通过缓存推荐库位,推荐的库位:{GetGroupOccupyNot[j].StorageNum}";
                        ////_logger.LogInformation(//messagelog);
                        return GetGroupOccupyNot[j].StorageNum;
                    }
                }
            }
        }
    #endregion

    getgroup:
        for (int k = 0; k < dataRegion.Count; k++)
        {
            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum,
                         x.StorageGroup
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                // 将缓存保存
                _cacheServices.Set(cachekey, data[0].StorageGroup);
                ////messagelog = $"⑨推荐的库位:{data[0].StorageNum}";
                ////_logger.LogInformation(//messagelog);
                return data[0].StorageNum.ToString();
            }

            #endregion
            // 查询是否有正在进行中的任务库位的组别
            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();
            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            ////messagelog = $"⑩当前推荐库位时，有任务的组别：{string.Join(",", dataStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            ////_logger.LogInformation(//messagelog);
            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 // 排除可以出库的组别
                                 .Where(a => !outBoundGroup.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();

            ////messagelog = $"⑪根据策略推荐出所有合适的组别：{string.Join(",", getStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            ////_logger.LogInformation(//messagelog);

            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位
                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                ////messagelog = $"⑫当前组已经占用的库位：{string.Join(",", AlreadyOccupied.Select(item => item.StorageNum.ToString()))}";
                ////_logger.LogInformation(//messagelog);

                // 如果当前组没有占用的库位
                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                    // 将缓存保存
                    _cacheServices.Set(cachekey, getStorageGroup[i].StorageGroup);
                    ////messagelog = $"⑬通过策略推荐库位：{datalist[0].StorageNum}";
                    ////_logger.ScopeContext(new Dictionary<object, object>
                    //        {
                    //            {"StorageGroup",  getStorageGroup[i].StorageGroup},
                    //            {"StorageNum", datalist[0].StorageNum},
                    //            {"AllStorageGroup", getStorageGroup}
                    //        });
                    ////_logger.LogInformation(//messagelog);
                    return datalist[0].StorageNum;
                }
                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                      it.StorageGroup
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            // 将缓存保存
                            _cacheServices.Set(cachekey, GetGroupOccupyNot[i].StorageGroup);
                            ////messagelog = $"⑭通过策略推荐的库位，组别为：{GetGroupOccupyNot[i].StorageGroup}，库位为：{GetGroupOccupyNot[j].StorageNum}";
                            ////_logger.LogInformation(//messagelog);
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
            }
            #region 如果通过筛选后没有合适的库位，就入到可以出库的组别中库位占用最少的
            // 如果通过筛选后没有合适的库位，就入到可以出库的组别中库位占用最少的
            int maxCount = 0;
            string maxGroup = null;
            for (int q = 0; q < outBoundGroup.Count; q++)
            {
                int storageCount = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == outBoundGroup[q])
                         .Where(x => x.StorageOccupy == 0)
                         .Select(x => x.StorageNum)
                         .Count();

                // 将占用库位最少的库位返回
                if (storageCount > maxCount)
                {
                    maxCount = storageCount;
                    maxGroup = outBoundGroup[q];
                }
            }
            if (maxGroup == null)
            {
                ////messagelog = $"没有合适的库位";
                ////_logger.LogInformation(//messagelog);
                return "没有合适的库位";
            }
            List<string> strings1 = new List<string>();
            ////messagelog = $"⑮通过筛选后没有合适的库位，入到可出库组别中库位占用最少，组别为：{maxGroup}";
            ////_logger.LogInformation(//messagelog);

            // 本组中能用的库位
            var canOccupyStorage = _Storage.AsQueryable()
                     .Where(x => x.StorageGroup == maxGroup)
                     .Where(x => x.StorageOccupy == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => x.StorageNum)
                     .ToList();

            ////messagelog = $"⑯本组中能用的库位：{string.Join(",", canOccupyStorage.Select(item => item.ToString()))}";
            ////_logger.LogInformation(//messagelog);
            // 本组中第一个被占用的库位
            var noCanOccupyStorage = _Storage.AsQueryable()
                    .Where(x => x.StorageGroup == maxGroup)
                    .Where(x => x.StorageOccupy == 1)
                    .OrderBy(x => x.StorageNum, OrderByType.Asc)
                    .Select(x => x.StorageNum)
                    .First();

            ////messagelog = $"⑯本组中第一个被占用的库位：{noCanOccupyStorage}";
            ////_logger.LogInformation(//messagelog);
            for (int z = 0; z < canOccupyStorage.Count; z++)
            {
                // 把所有能入库的库位保存
                if (noCanOccupyStorage.ToInt() > canOccupyStorage[z].ToInt())
                {
                    strings1.Add(canOccupyStorage[z]);
                }
                else
                {
                    continue;
                }
            }
            // 将缓存保存
            _cacheServices.Set(cachekey, maxGroup);
            ////messagelog = $"⑰通过策略推荐库位，推荐的库位为：{strings1[0]}";
            ////_logger.LogInformation(//messagelog);
            return strings1[0];
            #endregion
        }
        // 如果没有则返回错误
        ////messagelog = $"没有合适的库位";
        ////_logger.LogInformation(//messagelog);
        return "没有合适的库位";
    }

    #endregion

    #region （策略）（密集库）AGV入库WMS自动推荐的库位（如果没有合适的库位可以推荐组中占用最少的库位）《无缓存》

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位（如果没有合适的库位可以推荐组中占用最少的库位）《无缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommMinimumOccupancyEndStorage", Order = 1000)]
    public string AGVStrategyReturnRecommMinimumOccupancyEndStorage(string materielNum)
    {
        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        List<string> outBoundGroup = GetOutBoundStorageGroup(materielNum);
        //string messagelog = "";
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }

        for (int k = 0; k < dataRegion.Count; k++)
        {
            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                //messagelog = $"①推荐的库位:{data[0].StorageNum}";
                //_logger.LogInformation(//messagelog);
                return data[0].StorageNum.ToString();
            }

            #endregion
            // 查询是否有正在进行中的任务库位的组别
            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();
            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }
            //messagelog = $"②当前物料区域有任务的组别:{string.Join(",", dataStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            //_logger.LogInformation(//messagelog);

            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 // 排除可以出库的组别
                                 .Where(a => !outBoundGroup.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();
            //messagelog = $"③当前物料区域所有可用的组别:{string.Join(",", getStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            //_logger.LogInformation(//messagelog);

            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位
                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                // 如果当前组没有占用的库位
                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                    //messagelog = $"④根据策略推荐库位，组别为：{getStorageGroup[i].StorageGroup}，推荐的库位为：{datalist[0].StorageNum}";
                    //_logger.LogInformation(//messagelog);
                    return datalist[0].StorageNum;
                }
                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            //messagelog = $"⑤根据策略推荐库位，组别为：{allStorageOccupy.StorageGroup}，推荐的库位为：{GetGroupOccupyNot[j].StorageNum}，当前组别最后一个被占用的库位编号为：{lastOccupyNum}";
                            //_logger.LogInformation(//messagelog);
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
            }
            // 如果通过筛选后没有合适的库位，就入到可以出库的组别中库位占用最少的
            int maxCount = 0;
            string maxGroup = null;
            for (int q = 0; q < outBoundGroup.Count; q++)
            {
                int storageCount = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == outBoundGroup[q])
                         .Where(x => x.StorageOccupy == 0)
                         .Select(x => x.StorageNum)
                         .Count();

                // 将占用库位最少的库位返回
                if (storageCount > maxCount)
                {
                    maxCount = storageCount;
                    maxGroup = outBoundGroup[q];
                }
            }
            //messagelog = $"⑥没有合适库位，入到可出库库位占用最少的那组：{maxGroup}";
            //_logger.LogInformation(//messagelog);
            if (maxGroup == null)
            {
                //messagelog = $"⑨没有合适的库位";
                //_logger.LogInformation(//messagelog);
                return "没有合适的库位";
            }
            List<string> strings1 = new List<string>();

            // 本组中能用的库位
            var canOccupyStorage = _Storage.AsQueryable()
                     .Where(x => x.StorageGroup == maxGroup)
                     .Where(x => x.StorageOccupy == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => x.StorageNum)
                     .ToList();

            // 本组中第一个被占用的库位
            var noCanOccupyStorage = _Storage.AsQueryable()
                    .Where(x => x.StorageGroup == maxGroup)
                    .Where(x => x.StorageOccupy == 1)
                    .OrderBy(x => x.StorageNum, OrderByType.Asc)
                    .Select(x => x.StorageNum)
                    .First();
            //messagelog = $"⑦本组中可用库位：{string.Join(",", canOccupyStorage.Select(x => x.ToString()))}，本组中第一个被占用的库位为：{noCanOccupyStorage}";
            //_logger.LogInformation(//messagelog);
            for (int z = 0; z < canOccupyStorage.Count; z++)
            {
                // 把所有能入库的库位保存
                if (noCanOccupyStorage.ToInt() > canOccupyStorage[z].ToInt())
                {
                    strings1.Add(canOccupyStorage[z]);
                }
                else
                {
                    continue;
                }
            }
            //messagelog = $"⑧根据策略推荐库位，推荐的库位为：{strings1[0]}";
            //_logger.LogInformation(//messagelog);
            return strings1[0];
        }
        // 如果没有则返回错误
        //messagelog = $"⑨没有合适的库位";
        //_logger.LogInformation(//messagelog);
        return "没有合适的库位";
    }

    #endregion

    #region （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期最早的先出）《有缓存》

    /// <summary>
    /// （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期最早的先出）《有缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVCacheStrategyReturnRecommendEarliestStorageOutBoundJudgeTime", Order = 999)]
    public string AGVCacheStrategyReturnRecommendEarliestStorageOutBoundJudgeTime(string materielNum)
    {
        //string messagelog = "";
        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).Where(x => !x.RegionNum.Contains("RegionTM")).ToList();
        // 获得这个物料的需在库时间
        var materielData = _Materiel.GetFirst(x => x.MaterielNum == materielNum);
        // 用于保存每个区域里面的数据
        List<string> datastring = new List<string>();
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("物料未绑定区域");
        }

        #region 缓存组别
        // 缓存集合
        List<string> cachelist = _cacheServices.GetKeyList();
        IEnumerable<string> materielcache = cachelist.Where(x => { return x.Contains("Mat_OutBoundGroup_" + materielNum); });
        // 缓存key
        string cachekey = "";
        // 没有搜索到这个物料的缓存
        if (!materielcache.Any())
        {
            // 新建缓存
            cachekey = $"Mat_OutBoundGroup_{materielNum}";
            //messagelog = $"①新建了缓存，key：{cachekey}";
            //_logger.LogInformation(//messagelog);
            _cacheServices.Set(cachekey, null);
        }
        else
        {
            // 查询缓存里的数据
            object cachematerielvalue = _cacheServices.GetValue(materielcache.FirstOrDefault());
            cachekey = materielcache.FirstOrDefault();
            //messagelog = $"②查到了缓存数据，key：{cachekey}，value：{cachematerielvalue.ToString()}";
            //_logger.LogInformation(//messagelog);
            // 缓存里面有数据
            if (cachematerielvalue is string)
            {
                // PS:判断是不是空字符串，如果是空字符串需要跳出，不然会报错
                if (string.IsNullOrEmpty(cachematerielvalue.ToString()))
                {
                    _cacheServices.Set(cachekey, null);
                    goto getgroup;
                }
                // 判断出库的物料和缓存里面的物料一不一样
                string cacheregion = _Storage.AsQueryable().Where(x => x.StorageGroup == cachematerielvalue.ToString()).Select(x => x.RegionNum).First();
                var getregionmateriel = _Region.GetFirst(x => x.RegionNum == cacheregion);
                if (!(getregionmateriel.RegionMaterielNum.ToInt() == materielNum.ToInt()))
                {
                    // 将缓存保存
                    _cacheServices.Set(cachekey, null);
                    goto getgroup;
                }
                var beoccupiedstorage = _Storage.AsQueryable()
                .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                .Where(x => x.StorageStatus == 0)
                .Where(x => x.StorageOccupy == 2)
                .ToList();

                // 这列有任务
                if (beoccupiedstorage.Count != 0)
                {
                    //messagelog = $"③有任务等待任务完成";
                    //_logger.LogInformation(//messagelog);
                    return "等待任务完成！";
                }

                // 查询得到当前组别下有没有占用的库位
                var allStorageOccupy = _Storage.AsQueryable()
                         .Where(x => x.StorageOccupy == 1 && x.StorageGroup == cachematerielvalue.ToString())
                         .Where(x => x.StorageStatus == 0)
                         .OrderBy(a => a.StorageNum, OrderByType.Asc)
                         .Select(a => new
                         {
                             a.StorageNum,
                             a.StorageProductionDate
                         })
                         .ToList();

                //messagelog = $"④缓存组别下有占用的库位:{string.Join(",", allStorageOccupy.Select(x => x.StorageNum.ToString()))}";
                //_logger.LogInformation(//messagelog);
                // 还有被占用的库位
                if (allStorageOccupy.Count != 0)
                {
                    // 查询这组的第一个符不符合出库的条件
                    DateTime cachestoragedate = (DateTime)allStorageOccupy.First().StorageProductionDate;
                    // 加上需在库时间
                    DateTime needstoragetime = cachestoragedate.AddHours((double)materielData.MiniTime);
                    //messagelog = $"⑤需在库时间:{needstoragetime.ToString("yyyy-MM-dd")}";
                    //_logger.LogInformation(//messagelog);
                    if (needstoragetime < DateTime.Now)
                    {
                        // 将缓存保存
                        _cacheServices.Set(cachekey, cachematerielvalue.ToString());
                        //messagelog = $"⑥通过缓存推荐，组别为：{cachematerielvalue.ToString()}，推荐的库位为：{allStorageOccupy.First().StorageNum.ToString()}";
                        //_logger.LogInformation(//messagelog);
                        return allStorageOccupy.First().StorageNum.ToString();
                    }
                }
                else
                {
                    // 将缓存保存
                    _cacheServices.Set(cachekey, null);
                }
            }
        }
    #endregion

    getgroup:
        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别
            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            //messagelog = $"⑦本次请求时，正在进行任务的组别：{string.Join(",", dataStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            //_logger.LogInformation(//messagelog);
            // 查询所有的组别（排除不符合条件的组别）
            var getGroup = _Storage.AsQueryable()
                            .Where(x => !strings.Contains(x.StorageGroup) && x.StorageType == 0 &&
                                   x.StorageGroup != null && x.RegionNum == dataRegion[k].RegionNum)
                            .Distinct()
                            .Select(x => x.StorageGroup)
                            .ToList();

            // 如果这一组的最后一个的时间还没有达到，则这一组都用不了 
            string[] stringss = new string[getGroup.Count];
            var storagenum = new Entity.EG_WMS_Storage();
            for (int i = 0; i < getGroup.Count; i++)
            {
                var notStorageGroup = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == getGroup[i] && x.StorageOccupy == 1)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .ToList();
                if (notStorageGroup.Count != 0)
                {
                    storagenum = notStorageGroup.Last();
                }
                // 这个组的最后一条数据不符合条件，把这个组别保存下来
                if (storagenum.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) > DateTime.Now)
                {
                    stringss[i] = storagenum.StorageGroup.ToString();
                }
            }
            // 查询当前物料的入库组别
            IEnumerable<string> inboundgroupcache = cachelist.Where(x => { return x.Contains("Mat_InBoundGroup_" + materielNum); });
            object inboundgroup = "";
            if (inboundgroupcache.Any())
            {
                inboundgroup = _cacheServices.GetValue(inboundgroupcache.FirstOrDefault());
            }

            // 查询组别并且排除不符合条件的组别
            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum &&
                     !strings.Contains(a.StorageGroup) && !stringss.Contains(a.StorageGroup) &&
                     a.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) < DateTime.Now)
                     // 排除掉缓存入库的组别
                     .WhereIF(!string.IsNullOrEmpty(inboundgroup?.ToString()), a => !a.StorageGroup.Contains(inboundgroup.ToString()))
                     .OrderBy(a => a.StorageProductionDate, OrderByType.Asc)
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Distinct()
                     .Select(a => new
                     {
                         a.StorageGroup,
                     })
                     .ToList();

            //messagelog = $"⑧查询组别排除不符合条件的组别：{string.Join(",", getStorage.Select(x => x.StorageGroup.ToString()))}";
            //_logger.LogInformation(//messagelog);
            // 查询这个物料最早的生产日期
            var productdate = _Inventory.AsQueryable()
                       .Where(x => x.OutboundStatus == 0)
                       .Where(x => x.MaterielNum == materielNum)
                       .OrderBy(x => x.ProductionDate, OrderByType.Asc)
                       .Distinct()
                       .Select(x => x.ProductionDate)
                       .ToList();
            DateTime? firstDate = productdate.First();
            var firstDateAsString = firstDate.Value.ToString("yyyy-MM-dd");

            // 有库位
            if (getStorage.Count != 0)
            {
                for (int i = 0; i < getStorage.Count; i++)
                {
                    // 得到这个组别下所有的库位,并且按照生产日期排序
                    // 因为在前一步中，已经排序出来了每个组别最早的时间
                    // 现在的判断是这个组别中可以出库的时间，能不能出库，出库会不会被阻挡
                    var storageGetDateTime = _Storage.AsQueryable()
                                               .Where(x => x.StorageGroup == getStorage[i].StorageGroup)
                                               .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                                               .OrderBy(x => x.StorageProductionDate, OrderByType.Asc)
                                               .Select(x => x.StorageNum)
                                               .ToList();

                    var storageGetStorageNum = _Storage.AsQueryable()
                             .Where(x => x.StorageGroup == getStorage[i].StorageGroup)
                             .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Select(x => new { x.StorageNum, x.StorageProductionDate })
                             .ToList();

                    // 如果根据日期来出库的库位编号大于按照顺序来排序的库位编号
                    // 则代表这个库位出库会碰撞，则不能出库，需要重新选择库位
                    if (storageGetDateTime.First().ToInt() > storageGetStorageNum.First().StorageNum.ToInt())
                    {
                        // 在查询这一组有没有满足条件的库位
                        DateTime? dateTime1 = storageGetStorageNum.First().StorageProductionDate;
                        string datetiem1 = dateTime1.Value.ToString("yyyy-MM-dd");
                        if (datetiem1 == firstDateAsString)
                        {
                            // 将缓存保存
                            _cacheServices.Set(cachekey, getStorage[i].StorageGroup);
                            //messagelog = $"⑨策略推荐库位组别为：{getStorage[i].StorageGroup}推荐的库位为：{storageGetStorageNum.First().StorageNum.ToString()}";
                            //_logger.LogInformation(//messagelog);
                            return storageGetStorageNum.First().StorageNum.ToString();
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        // 将缓存保存
                        _cacheServices.Set(cachekey, getStorage[i].StorageGroup);
                        //messagelog = $"⑨策略推荐库位组别为：{getStorage[i].StorageGroup}推荐的库位为：{storageGetDateTime.First().ToString()}";
                        //_logger.LogInformation(//messagelog);
                        return storageGetDateTime.First().ToString();
                    }
                }
                // TODO：有问题
                // 如果里面的时间更早，外面的时间更晚，但是能出库，外面的也出不了
                // 应该让出最外面的库位,就让出根据时间排序最外面的那个
                var outsideStorage = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorage[0].StorageGroup)
                         .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                         .OrderBy(x => x.StorageNum, OrderByType.Asc)
                         .Select(x => new { x.StorageNum, x.StorageGroup })
                         .ToList();

                // 将缓存保存
                _cacheServices.Set(cachekey, getStorage[0].StorageGroup);
                //messagelog = $"⑨策略推荐库位组别为，出根据时间排序最外面的那个：{getStorage[0].StorageGroup}推荐的库位为：{outsideStorage.First().StorageNum.ToString()}";
                //_logger.LogInformation(//messagelog);
                return outsideStorage.First().StorageNum.ToString();
            }
            // 如果没有组别，判断缓存入库的组别是不是可以出库了
            else if (getStorage.Count == 0)
            {
                List<string> canoutboundgrouplist = GetOutBoundStorageGroup(materielNum);
                // 能出库的组别是缓存入库的组别
                if (canoutboundgrouplist.Contains(inboundgroup.ToString()))
                {
                    //修改入库的缓存，设置为null
                    _cacheServices.Set(inboundgroupcache.FirstOrDefault(), null);
                    //修改出库的缓存,并且返回库位
                    _cacheServices.Set(materielcache.FirstOrDefault(), inboundgroup.ToString());
                    return _Storage.AsQueryable()
                        .Where(x => x.StorageGroup == inboundgroup.ToString())
                        .Where(x => x.StorageOccupy == 1)
                        .OrderBy(x => x.StorageNum, OrderByType.Asc)
                        .Select(x => x.StorageNum)
                        .First()
                        .ToString();
                }
            }
            else
            {
                continue;
            }
        }
        //messagelog = $"⑩没有合适的库位";
        //_logger.LogInformation(//messagelog);
        _cacheServices.Set(cachekey, null);
        return "没有合适的库位";
        // 将缓存保存
    }


    #endregion

    #region （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期最早的先出）《无缓存》

    /// <summary>
    /// （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期最早的先出）《无缓存》
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommendEarliestStorageOutBoundJudgeTime", Order = 999)]
    public string AGVStrategyReturnRecommendEarliestStorageOutBoundJudgeTime(string materielNum)
    {

        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        // 获得这个物料的需在库时间
        var materielData = _Materiel.GetFirst(x => x.MaterielNum == materielNum);
        // 用于保存每个区域里面的数据
        List<string> datastring = new List<string>();
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("物料未绑定区域");
        }
        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别
            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询所有的组别（排除不符合条件的组别）
            var getGroup = _Storage.AsQueryable()
                            .Where(x => !strings.Contains(x.StorageGroup) && x.StorageType == 0 &&
                                   x.StorageGroup != null && x.RegionNum == dataRegion[k].RegionNum)
                            .Distinct()
                            .Select(x => x.StorageGroup)
                            .ToList();

            // 如果这一组的最后一个的时间还没有达到，则这一组都用不了 
            string[] stringss = new string[getGroup.Count];
            var storagenum = new Entity.EG_WMS_Storage();
            for (int i = 0; i < getGroup.Count; i++)
            {
                var notStorageGroup = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == getGroup[i] && x.StorageOccupy == 1)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .ToList();
                if (notStorageGroup.Count != 0)
                {
                    storagenum = notStorageGroup.Last();
                }
                // 这个组的最后一条数据不符合条件，把这个组别保存下来
                if (storagenum.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) > DateTime.Now)
                {
                    stringss[i] = storagenum.StorageGroup.ToString();
                }
            }
            // 查询组别并且排除不符合条件的组别
            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum &&
                     !strings.Contains(a.StorageGroup) && !stringss.Contains(a.StorageGroup) &&
                     a.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) < DateTime.Now)
                     .OrderBy(a => a.StorageProductionDate, OrderByType.Asc)
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Distinct()
                     .Select(a => new
                     {
                         a.StorageGroup,
                     })
                     .ToList();

            // 查询这个物料最早的生产日期
            var productdate = _Inventory.AsQueryable()
                       .Where(x => x.OutboundStatus == 0)
                       .Where(x => x.MaterielNum == materielNum)
                       .OrderBy(x => x.ProductionDate, OrderByType.Asc)
                       .Distinct()
                       .Select(x => x.ProductionDate)
                       .ToList();
            DateTime? firstDate = productdate.First();
            var firstDateAsString = firstDate.Value.ToString("yyyy-MM-dd");

            // 有库位
            if (getStorage.Count != 0)
            {
                for (int i = 0; i < getStorage.Count; i++)
                {
                    // 得到这个组别下所有的库位,并且按照生产日期排序
                    // 因为在前一步中，已经排序出来了每个组别最早的时间
                    // 现在的判断是这个组别中可以出库的时间，能不能出库，出库会不会被阻挡
                    var storageGetDateTime = _Storage.AsQueryable()
                                               .Where(x => x.StorageGroup == getStorage[i].StorageGroup)
                                               .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                                               .OrderBy(x => x.StorageProductionDate, OrderByType.Asc)
                                               .Select(x => x.StorageNum)
                                               .ToList();

                    var storageGetStorageNum = _Storage.AsQueryable()
                             .Where(x => x.StorageGroup == getStorage[i].StorageGroup)
                             .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Select(x => new { x.StorageNum, x.StorageProductionDate })
                             .ToList();

                    // 如果根据日期来出库的库位编号大于按照顺序来排序的库位编号
                    // 则代表这个库位出库会碰撞，则不能出库，需要重新选择库位
                    if (storageGetDateTime.First().ToInt() > storageGetStorageNum.First().StorageNum.ToInt())
                    {
                        // 在查询这一组有没有满足条件的库位
                        DateTime? dateTime1 = storageGetStorageNum.First().StorageProductionDate;
                        string datetiem1 = dateTime1.Value.ToString("yyyy-MM-dd");
                        if (datetiem1 == firstDateAsString)
                        {
                            return storageGetStorageNum.First().StorageNum.ToString();
                        }
                        else
                        {
                            continue;
                        }
                    }
                    else
                    {
                        return storageGetDateTime.First().ToString();
                    }
                }
                // TODO：有问题
                // 如果里面的时间更早，外面的时间更晚，但是能出库，外面的也出不了
                // 应该让出最外面的库位,就让出根据时间排序最外面的那个
                var outsideStorage = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorage[0].StorageGroup)
                         .Where(x => x.StorageOccupy == 1 && x.StorageStatus == 0)
                         .OrderBy(x => x.StorageNum, OrderByType.Asc)
                         .Select(x => x.StorageNum)
                         .ToList();
                return outsideStorage.First().ToString();
            }
            else
            {
                continue;
            }
        }
        return "没有合适的库位";
    }


    #endregion

    #region （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期）

    /// <summary>
    /// （策略）（密集库）AGV出库WMS自动推荐的库位（判断生产日期）
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommendStorageOutBoundJudgeTime", Order = 999)]
    public string AGVStrategyReturnRecommendStorageOutBoundJudgeTime(string materielNum)
    {

        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        // 用于保存每个区域里面的数据
        List<string> datastring = new List<string>();
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }
        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询所有的组别（排除不符合条件的组别）
            var getGroup = _Storage.AsQueryable()
                            .Where(x => !strings.Contains(x.StorageGroup) && x.StorageType == 0 &&
                                   x.StorageGroup != null && x.RegionNum == dataRegion[k].RegionNum)
                            .Distinct()
                            .Select(x => x.StorageGroup)
                            .ToList();

            // 如果这一组的最后一个的时间还没有达到，则这一组都用不了 
            string[] stringss = new string[getGroup.Count];
            var storagenum = new Entity.EG_WMS_Storage();
            for (int i = 0; i < getGroup.Count; i++)
            {
                var notStorageGroup = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == getGroup[i] && x.StorageOccupy == 1)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .ToList();

                if (notStorageGroup.Count != 0)
                {
                    storagenum = notStorageGroup.Last();
                }

                // 这个组的最后一条数据不符合条件，把这个组别保存下来
                if (storagenum.StorageProductionDate.ToDateTime().AddHours(48) > DateTime.Now)
                {
                    stringss[i] = storagenum.StorageGroup.ToString();
                }

            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum &&
                     !strings.Contains(a.StorageGroup) && !stringss.Contains(a.StorageGroup) &&
                     a.StorageProductionDate.ToDateTime().AddHours(48) < DateTime.Now)
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Select(a => new
                     {
                         a.StorageNum,
                         a.StorageGroup,
                     })
                     .ToList();

            // 将每个区域里面符合条件的库位保存
            foreach (var item in getStorage)
            {
                datastring.Add(item.StorageNum);
            }

        }

        // 将得到的库位重新进行排序，让最小编号的库位在前面
        List<int> dataInt = new List<int>();
        foreach (string s in datastring)
        {
            dataInt.Add(int.Parse(s));
        }
        dataInt.Sort();
        if (dataInt == null || dataInt.Count == 0)
        {
            //throw Oops.Oh("没有合适的库位");
            return "没有合适的库位";
        }

        return dataInt[0].ToString();

    }
    #endregion

    #region（策略）（密集库）AGV入库WMS自动推荐的库位（尾料入库推荐的库位）

    /// <summary>
    /// （策略）（密集库）AGV入库WMS自动推荐的库位（尾料入库推荐的库位）
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVTailMaterialStrategyReturnRecommEndStorage", Order = 1000)]
    public string AGVTailMaterialStrategyReturnRecommEndStorage(string materielNum)
    {
        // 根据物料编号，得到这个物料属于大料框尾料的哪个区域
        var dataRegion = _Region.AsQueryable()
                                .InnerJoin<Entity.EG_WMS_Storage>((a, b) => a.RegionNum == b.RegionNum)
                                .Where((a, b) => b.StorageName.Contains("大料框尾料WL"))
                                .Where((a, b) => a.RegionMaterielNum == materielNum)
                                .Distinct()
                                .ToList();

        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }

        for (int k = 0; k < dataRegion.Count; k++)
        {

            #region 用于新区域时，第一次入库推荐使用（可能需要修改）

            // 一开始初始化数据，第一个开始
            var data = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == dataRegion[k].RegionNum && x.StorageOccupy == 0 && x.StorageStatus == 0)
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => new
                     {
                         x.StorageNum
                     })
                     .ToList();

            // 区域库位总数
            int datacount = _Storage.AsQueryable()
                        .Where(x => x.RegionNum == dataRegion[k].RegionNum)
                        .Count();

            if (data.Count == datacount)
            {
                return data[0].StorageNum.ToString();
            }

            #endregion


            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];

            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                     .OrderBy(a => a.StorageNum, OrderByType.Desc)
                     .Select(a => new
                     {
                         a.StorageNum,
                         a.StorageGroup,
                     })
                     .ToList();


            // 得到组别
            var getStorageGroup = _Storage.AsQueryable()
                                 .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                                 && a.StorageOccupy == 0 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                                 .OrderBy(a => a.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(a => new
                                 {
                                     a.StorageGroup,
                                 })
                                 .ToList();


            for (int i = 0; i < getStorageGroup.Count; i++)
            {
                // 查询得到当前组已经占用的库位

                var AlreadyOccupied = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageOccupy == 1)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                // 如果当前组没有占用的库位

                if (AlreadyOccupied.Count == 0)
                {
                    var datalist = _Storage.AsQueryable()
                         .Where(x => x.StorageGroup == getStorageGroup[i].StorageGroup && x.StorageStatus == 0)
                         // TODO、没有验证
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Select(it => new
                         {
                             it.StorageNum,
                         })
                         .ToList();

                    return datalist[0].StorageNum;
                }

                // 如果在确实有占用的库位
                if (AlreadyOccupied.Count != 0)
                {
                    // 查询得到当前组别下最末尾一个有占用的库位
                    var allStorageOccupy = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 1 && x.StorageGroup == getStorageGroup[i].StorageGroup)
                             .OrderBy(a => a.StorageNum, OrderByType.Desc)
                             .Select(a => new
                             {
                                 a.StorageNum,
                                 a.StorageGroup,
                             })
                             .ToList()
                             .Last();

                    // 得到这个组别下所有的未占用库位
                    var GetGroupOccupyNot = _Storage.AsQueryable()
                                  .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                         x.StorageGroup == allStorageOccupy.StorageGroup)
                                  .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                  .Select(it => new
                                  {
                                      it.StorageNum,
                                  })
                                  .ToList();

                    // 依次判断符合条件的数据
                    // 当前组别最后一个被占用的库位编号
                    int lastOccupyNum = allStorageOccupy.StorageNum.ToInt();

                    for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                    {
                        if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                        {
                            return GetGroupOccupyNot[j].StorageNum;
                        }
                    }
                }
                else
                {
                    //throw Oops.Oh("没有合适的库位");
                    return "没有合适的库位";

                }
            }
        }

        // 如果没有则返回错误
        return "没有合适的库位";

        //throw Oops.Oh("没有合适的库位");
    }

    #endregion

    #region （策略）（密集库）AGV出库WMS自动推荐的库位（不判断生产日期）

    /// <summary>
    /// （策略）（密集库）AGV出库WMS自动推荐的库位（不判断生产日期）
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStrategyReturnRecommendStorageOutBound", Order = 998)]
    public string AGVStrategyReturnRecommendStorageOutBound(string materielNum)
    {
        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).Where(x => !x.RegionNum.Contains("RegionTM")).ToList();
        // 用于保存每个区域里面的数据
        List<string> datastring = new List<string>();

        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }

        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Select(a => new
                     {
                         a.StorageNum,
                     })
                     .ToList();

            // 将每个区域里面符合条件的库位保存

            foreach (var item in getStorage)
            {
                datastring.Add(item.StorageNum);
            }
        }
        // 将得到的库位重新进行排序，让最小编号的库位在前面

        List<int> dataInt = new List<int>();
        foreach (string s in datastring)
        {
            dataInt.Add(int.Parse(s));
        }
        dataInt.Sort();

        if (dataInt == null || dataInt.Count == 0)
        {
            return "没有合适的库位";
        }
        return dataInt[0].ToString();
    }


    #endregion

    #region （策略）（密集库）AGV尾料出库WMS自动推荐的库位（不判断生产日期）

    /// <summary>
    /// （策略）（密集库）AGV尾料出库WMS自动推荐的库位（不判断生产日期）
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVTailMaterialStrategyReturnRecommendStorageOutBound", Order = 998)]
    public string AGVTailMaterialStrategyReturnRecommendStorageOutBound(string materielNum)
    {
        // 根据物料编号，得到这个物料属于大料框尾料的哪个区域
        var dataRegion = _Region.AsQueryable()
                                .InnerJoin<Entity.EG_WMS_Storage>((a, b) => a.RegionNum == b.RegionNum)
                                .Where((a, b) => b.StorageName.Contains("大料框尾料"))
                                .Where((a, b) => a.RegionMaterielNum == materielNum)
                                .Distinct()
                                .ToList();

        // 用于保存每个区域里面的数据
        List<string> datastring = new List<string>();

        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }
        //string messagelog = "";
        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();
            //messagelog = $"①当前本物料正在进行任务的组别：{string.Join(",", dataStorageGroup.Select(x => x.StorageGroup.ToString()))}";
            //_logger.LogInformation(//messagelog);
            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum && !strings.Contains(a.StorageGroup))
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Select(a => new
                     {
                         a.StorageNum,
                     })
                     .ToList();
            //messagelog = $"②得到所有符合条件的库位：{string.Join(",", getStorage.Select(x => x.StorageNum.ToString()))}";
            //_logger.LogInformation(//messagelog);
            // 将每个区域里面符合条件的库位保存

            foreach (var item in getStorage)
            {
                datastring.Add(item.StorageNum);
            }
        }
        // 将得到的库位重新进行排序，让最小编号的库位在前面

        List<int> dataInt = new List<int>();
        foreach (string s in datastring)
        {
            dataInt.Add(int.Parse(s));
        }
        dataInt.Sort();

        if (dataInt == null || dataInt.Count == 0)
        {
            return "没有合适的库位";
        }
        //messagelog = $"③通过策略推荐，推荐的库位为：{string.Join(",", dataInt[0].ToString())}";
        //_logger.LogInformation(//messagelog);
        return dataInt[0].ToString();
    }

    #endregion

    #region （策略一）（立库）堆高车入库WMS自动推荐的库位（按照库位编号大小：从小到大依次开始推荐，不需要根据物料产品）

    /// <summary>
    /// （策略一）（立库）堆高车入库WMS自动推荐的库位（按照库位编号大小：从小到大依次开始推荐，不需要根据物料产品）
    /// </summary>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStacKingHighCarsIntoReturnStorage", Order = 997)]
    public string AGVStacKingHighCarsIntoReturnStorage()
    {

        var storagenum = _Storage.AsQueryable()
                 .Where(x => x.StorageType == 1 && x.StorageOccupy == 0 && x.StorageStatus == 0)
                 .OrderBy(x => x.StorageNum, OrderByType.Asc)
                 .ToList()
                 .First();

        if (storagenum == null)
        {
            return "当前没有合适的库位！";
        }
        return storagenum.StorageNum.ToString();

    }


    #endregion

    #region （策略二）（立库）堆高车入库WMS自动推荐的库位（按照库位编号大小：从小到大依次开始推荐，不需要根据物料产品）

    #endregion

    #region （策略）（立库）堆高车出库WMS自动推荐的库位（根据先入先出原则以及出库总数）

    /// <summary>
    /// （策略）（立库）堆高车出库WMS自动推荐的库位（根据先入先出原则以及出库总数）
    /// </summary>
    /// <param name="materielNum">物料编号</param>
    /// <param name="quantity">出库总数</param>
    /// <returns></returns>
    [HttpPost]
    [ApiDescriptionSettings(Name = "AGVStackingHighCarStorageOutBound", Order = 996)]
    public List<string> AGVStackingHighCarStorageOutBound(string materielNum, int quantity)
    {

        // 找到这个物料在库存中存在的
        var invdata = _Inventory.AsQueryable()
                                .InnerJoin<EG_WMS_InventoryDetail>((a, b) => a.InventoryNum == b.InventoryNum)
                                .InnerJoin<Entity.EG_WMS_Storage>((a, b, c) => b.StorageNum == c.StorageNum)
                                .Where((a, b, c) => a.MaterielNum == materielNum && a.OutboundStatus == 0 && a.IsDelete == false && c.StorageType == 1)
                                .OrderBy((a, b) => a.CreateTime, OrderByType.Asc) // 从小到大排序
                                .Select((a, b) => new { a.ICountAll, b.StorageNum })
                                .ToList();

        if (invdata.Count == 0 || invdata == null)
        {
            throw Oops.Oh("当前库存中没有存在该物料的库存信息！");
        }

        List<string> result = new List<string>();
        int? sumcount = 0;
        for (int i = 0; i < invdata.Count; i++)
        {
            // 第一种情况（输入的数量恰好等于或大于第一个库位上的数量）
            if (invdata[i].ICountAll >= quantity)
            {
                result.Clear();
                result.Add(invdata[i].StorageNum.ToString());
                break;
            }
            else if (invdata[i].ICountAll < quantity)
            {
                if (sumcount >= quantity)
                {
                    break;
                }
                sumcount += invdata[i].ICountAll;
                result.Add(invdata[i].StorageNum.ToString());
            }
            else if (i == invdata.Count - 1)
            {
                if (sumcount < quantity)
                {
                    throw Oops.Oh("当前在库中该物料数量不足出库数量！");
                }
            }
        }
        return result;
    }


    #endregion

    #region （策略）AGV请求任务点
    /// <summary>
    /// （策略）（密集库）AGV请求任务点（到达等待点获取）
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [UnifyProvider("easygreat")]
    [ApiDescriptionSettings(Name = "AGVRequestsTaskPoint", Order = 995)]
    public object AGVRequestsTaskPoint(GetPointDto input)
    {
        using (_mutex.Lock())
        {
            // 找到这个任务编号相同和模板编号相同的这条数据
            var taskData = _TaskEntity.GetFirst(x => x.TaskNo == input.orderId && x.ModelNo == input.modelProcessCode);
            if (taskData == null)
            {
                throw Oops.Oh("没有找到相同的任务编号或模板编号");
            }
            // 根据这个任务编号得到入库编号
            var inand = _InAndOutBoundDetail.GetFirst(x => x.InAndOutBoundNum == taskData.InAndOutBoundNum);
            string malnum = "";
            //string messagelog = "";
            // 入空框
            if (inand.InAndOutBoundNum.Contains("EGEFRK"))
            {
                // 根据策略推荐出空框的库位
                string SmallFrameStorageNum = AGVAniseedFrameDynamicSwitchingEndStorage(inand.MaterielNum);
                if (!int.TryParse(SmallFrameStorageNum, out int vale))
                {
                    throw Oops.Oh("当前没有合适的库位！");
                }
                // 修改agv任务表里面的库位点
                var _taskdata = _TaskEntity.GetFirst(x => x.TaskNo == input.orderId);
                string path = _taskdata.TaskPath;
                string[] result = path.Split(',');
                string taskpath = string.Join(",", result[0], result[1], SmallFrameStorageNum);
                // 根据库位查询区域
                string regiondata = boundMessage.GetStorageWhereRegion(SmallFrameStorageNum);
                string whousedata = boundMessage.GetRegionWhereWHNum(regiondata);
                #region 修改
                _TaskEntity.AsUpdateable()
                          .SetColumns(it => new TaskEntity
                          {
                              TaskPath = taskpath,
                          })
                          .Where(x => x.TaskNo == input.orderId)
                          .ExecuteCommand();

                // 修改库位表中的状态为占用
                _Storage.AsUpdateable()
                         .AS("EG_WMS_Storage")
                         .SetColumns(it => new Entity.EG_WMS_Storage
                         {
                             // 预占用
                             StorageOccupy = 2,
                             TaskNo = taskData.TaskNo,
                             StorageNum = SmallFrameStorageNum,
                         })
                         .Where(x => x.StorageNum == SmallFrameStorageNum)
                         .ExecuteCommand();

                // 修改入库表里面的库位编号
                _InAndOutBound.AsUpdateable()
                              .SetColumns(it => new Entity.EG_WMS_InAndOutBound
                              {
                                  EndPoint = SmallFrameStorageNum,
                                  UpdateTime = DateTime.Now,
                              })
                              .Where(x => x.InAndOutBoundNum == taskData.InAndOutBoundNum)
                              .ExecuteCommand();

                _InAndOutBoundDetail.AsUpdateable()
                                    .SetColumns(it => new EG_WMS_InAndOutBoundDetail
                                    {
                                        WHNum = whousedata,
                                        RegionNum = regiondata,
                                        StorageNum = SmallFrameStorageNum,
                                        UpdateTime = DateTime.Now,
                                    })
                                    .Where(x => x.InAndOutBoundNum == taskData.InAndOutBoundNum)
                                    .ExecuteCommand();
                #endregion
                // 添加agv详情表里面的库位点
                TaskDetailEntity entity = new TaskDetailEntity();
                entity.TaskID = input.orderId.ToLong();
                entity.TaskPath = SmallFrameStorageNum;
                entity.CreateTime = DateTime.Now;
                _TaskDetailEntity.Insert(entity);
                //messagelog = $"任务单号为：{input.orderId}，使用这个方法：AGVAniseedFrameDynamicSwitchingEndStorage";
                //_logger.LogInformation(//messagelog);
                return new
                {
                    PointName = SmallFrameStorageNum,
                };
            }
            var materieldata = _Materiel.GetFirst(x => x.MaterielNum == inand.MaterielNum);
            // 有动态库位（机芯、PBT机芯）和固定库位（机芯）
            if (inand.InAndOutBoundNum.Contains("EGRK") && materieldata.MaterielType.Contains("小料框") && (materieldata.MaterielNum == "03980010008" || materieldata.MaterielNum == "03980010025"))
            {
                //messagelog = $"任务单号为：{input.orderId}，使用这个方法：AGVCacheAniseedFrameDynamicSwitchingEndStorage";
                //_logger.LogInformation(//messagelog);
                malnum = AGVCacheAniseedFrameDynamicSwitchingEndStorage(inand.MaterielNum);
            }
            else if (inand.InAndOutBoundNum.Contains("EGRK") && materieldata.MaterielType.Contains("小料框"))
            {
                //messagelog = $"任务单号为：{input.orderId}，使用这个方法：AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage";
                //_logger.LogInformation(//messagelog);
                malnum = AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage(inand.MaterielNum);
            }
            else if (inand.InAndOutBoundNum.Contains("EGRK") && materieldata.MaterielType.Contains("大料框"))
            {
                //messagelog = $"任务单号为：{input.orderId}，使用这个方法：AGVCacheAniseedFrameDynamicSwitchingEndStorage";
                //_logger.LogInformation(//messagelog);
                malnum = AGVCacheAniseedFrameDynamicSwitchingEndStorage(inand.MaterielNum);
            }
            // 入尾料
            else if (inand.InAndOutBoundNum.Contains("EGTMRK") && materieldata.MaterielType.Contains("大料框"))
            {
                //messagelog = $"任务单号为：{input.orderId}，使用这个方法：AGVTailMaterialAniseedFrameDynamicSwitchingEndStorage";
                //_logger.LogInformation(//messagelog);
                malnum = AGVTailMaterialAniseedFrameDynamicSwitchingEndStorage(inand.MaterielNum);
            }
            else
            {
                throw Oops.Oh("策略出现错误，请联系管理员！");
            }
            if (!int.TryParse(malnum, out int val))
            {
                throw Oops.Oh("没有合适的库位");
            }
            string regionnum = boundMessage.GetStorageWhereRegion(malnum);
            string whnum = boundMessage.GetRegionWhereWHNum(regionnum);
            #region 根据得到的库位编号去修改入库的库位
            using (TransactionScope scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
            {
                try
                {
                    // 修改agv任务表里面的库位点
                    var _taskdata = _TaskEntity.GetFirst(x => x.TaskNo == input.orderId);

                    string path = _taskdata.TaskPath;
                    string[] result = path.Split(',');
                    string taskpath = string.Join(",", result[0], result[1], malnum);

                    _TaskEntity.AsUpdateable()
                               .SetColumns(it => new TaskEntity
                               {
                                   TaskPath = taskpath,
                               })
                               .Where(x => x.TaskNo == input.orderId)
                               .ExecuteCommand();

                    // 添加agv详情表里面的库位点
                    TaskDetailEntity entity = new TaskDetailEntity();
                    entity.TaskID = input.orderId.ToLong();
                    entity.TaskPath = malnum;
                    entity.CreateTime = DateTime.Now;

                    _TaskDetailEntity.Insert(entity);

                    // 得到入库的数据

                    var temInvData = _TemInventory.AsQueryable()
                             .Where(x => x.InBoundNum == taskData.InAndOutBoundNum)
                             .Select(x => new
                             {
                                 x.ProductionDate
                             })
                             .ToList();

                    // 将生产日期保存
                    List<DateTime?> temInvTime = new List<DateTime?>();
                    for (int i = 0; i < temInvData.Count; i++)
                    {
                        temInvTime.Add(temInvData[i].ProductionDate);
                    }

                    // 修改库位表中的状态为占用
                    _Storage.AsUpdateable()
                             .AS("EG_WMS_Storage")
                             .SetColumns(it => new Entity.EG_WMS_Storage
                             {
                                 // 预占用
                                 StorageOccupy = 2,
                                 TaskNo = taskData.TaskNo,
                                 // 得到日期最大的生产日期
                                 StorageProductionDate = temInvTime.Max(),
                                 StorageNum = malnum,
                             })
                             .Where(x => x.StorageNum == malnum)
                             .ExecuteCommand();

                    // 根据入库编号去修改库位

                    _InAndOutBound.AsUpdateable()
                       .AS("EG_WMS_InAndOutBound")
                       .SetColumns(it => new Entity.EG_WMS_InAndOutBound
                       {
                           EndPoint = malnum,
                       })
                       .Where(u => u.InAndOutBoundNum == taskData.InAndOutBoundNum)
                       .ExecuteCommand();

                    _InAndOutBoundDetail.AsUpdateable()
                        .AS("EG_WMS_InAndOutBoundDetail")
                        .SetColumns(it => new EG_WMS_InAndOutBoundDetail
                        {
                            StorageNum = malnum,
                            RegionNum = regionnum,
                            WHNum = whnum,
                        })
                        .Where(u => u.InAndOutBoundNum == taskData.InAndOutBoundNum)
                        .ExecuteCommand();


                    // 根据入库编号得到临时库位表里面的库存编号
                    var dataTemList = _TemInventory.AsQueryable()
                                   .Where(u => u.InBoundNum == taskData.InAndOutBoundNum)
                                   .Select(x => x.InventoryNum)
                                   .ToList();

                    for (int i = 0; i < dataTemList.Count; i++)
                    {
                        _TemInventoryDetail.AsUpdateable()
                                        .AS("EG_WMS_Tem_InventoryDetail")
                                        .SetColumns(it => new Entity.EG_WMS_Tem_InventoryDetail
                                        {
                                            StorageNum = malnum,
                                            RegionNum = regionnum,
                                            WHNum = whnum,
                                        })
                                        .Where(u => u.InventoryNum == dataTemList[i])
                                        .ExecuteCommand();
                    }

                    // 提交事务
                    scope.Complete();
                }
                catch (Exception ex)
                {
                    // 回滚事务
                    scope.Dispose();
                    throw Oops.Oh("错误：" + ex.Message);
                }
            }
            #endregion
            return new
            {
                PointName = malnum,
            };
        }
    }

    #endregion

    #region 得到所有立库上面有料箱数据的

    /// <summary>
    /// 得到所有立库上面有料箱数据的
    /// </summary>
    /// <returns></returns>
    public List<string> GetWorkBinAllDatas()
    {

        return _Storage.AsQueryable()
                     .Where(x => x.StorageOccupy == 1 && x.StorageType == 1)
                     .Select(x => x.StorageNum)
                     .ToList();

    }


    #endregion

    #region （策略）入库WMS自动推荐的库位（动态转换库位）《有缓存》

    /// <summary>
    /// （策略）入库WMS自动推荐的库位（动态转换库位）《有缓存》
    /// </summary>
    /// <param name="MaterielNum"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVCacheAniseedFrameDynamicSwitchingEndStorage")]
    public string AGVCacheAniseedFrameDynamicSwitchingEndStorage(string MaterielNum)
    {
        // 查询这个物料是那种类型的物料（常用还是不常用）
        var getmateriel = _Materiel.GetFirst(x => x.MaterielNum == MaterielNum);
        ////messagelogData //messagelogdata = new //messagelogData();
        // 日志
        //string messagelog = "";
        #region 缓存组别
        // 缓存集合
        List<string> cachelist = _cacheServices.GetKeyList();
        IEnumerable<string> materielcache = cachelist.Where(x => { return x.Contains("Mat_InBoundGroup_" + MaterielNum); });
        // 缓存key
        string cachekey = "";
        // 没有搜索到这个物料的缓存
        if (!materielcache.Any())
        {
            // 新建缓存
            cachekey = $"Mat_InBoundGroup_{MaterielNum}";
            //messagelog = $"①新建了缓存,key：{cachekey}";
            //_logger.LogInformation(//messagelog);
            _cacheServices.Set(cachekey, null);
        }
        else
        {
            // 查询缓存里的数据
            object cachematerielvalue = _cacheServices.GetValue(materielcache.FirstOrDefault());
            cachekey = materielcache.FirstOrDefault();
            //messagelog = $"②查到了缓存数据,key:{cachekey},value:{cachematerielvalue.ToString()}";
            //_logger.LogInformation(//messagelog);
            // 缓存里面有数据
            if (cachematerielvalue is string)
            {
                // 本组任务中的库位
                var beoccupiedstorage = _Storage.AsQueryable()
                .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                .Where(x => x.StorageStatus == 0)
                .Where(x => x.StorageOccupy == 2)
                .ToList();
                // 这列有任务
                if (beoccupiedstorage.Count != 0)
                {
                    //messagelog = $"③有任务等待任务完成";
                    //_logger.LogInformation(//messagelog);
                    return "等待任务完成！";
                }

                // 查询得到当前组别下有占用的库位
                var allStorageOccupy = _Storage.AsQueryable()
                         .Where(x => x.StorageOccupy == 1 && x.StorageGroup == cachematerielvalue.ToString())
                         .Where(x => x.StorageStatus == 0)
                         .OrderBy(a => a.StorageNum, OrderByType.Desc)
                         .Select(a => new
                         {
                             a.StorageNum,
                             a.StorageGroup,
                         })
                         .ToList();

                //messagelog = $"④缓存组别下有占用的库位:{string.Join(",", allStorageOccupy.Select(x => x.StorageNum.ToString()))}";
                //_logger.LogInformation(//messagelog);

                // 这组没有被占用的库位
                if (allStorageOccupy.Count == 0)
                {
                    var InBoundStorage = _Storage.AsQueryable()
                              .Where(x => x.StorageGroup == cachematerielvalue.ToString())
                              .Where(x => x.StorageStatus == 0)
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(x => new
                              {
                                  x.StorageNum,
                                  x.StorageGroup
                              })
                              .ToList();

                    // 将缓存保存
                    _cacheServices.Set(cachekey, cachematerielvalue.ToString());
                    //messagelog = $"⑤通过缓存推荐库位,本组所有的库位:{string.Join(",", InBoundStorage.Select(x => x.StorageNum.ToString()))}";
                    //_logger.LogInformation(//messagelog);
                    return InBoundStorage.First().StorageNum.ToString();
                }

                // TODO:
                // 调用其他接口排除能出库的
                // 如果能出库的组别在缓存中，则需要重新筛选缓存
                //List<string> canoutboundlist = GetOutBoundStorageGroup(MaterielNum);
                //if (canoutboundlist.Contains(cachematerielvalue.ToString()))
                //{
                //    _cacheServices.Set(cachekey, null);
                //    goto getgroup;
                //}   

                // 得到这个组别下所有的未占用库位
                var GetGroupOccupyNot = _Storage.AsQueryable()
                              .Where(x => x.StorageOccupy == 0 && x.StorageStatus == 0 &&
                                     x.StorageGroup == cachematerielvalue.ToString())
                              .OrderBy(x => x.StorageNum, OrderByType.Desc)
                              .Select(it => new
                              {
                                  it.StorageNum,
                                  it.StorageGroup
                              })
                              .ToList();

                //messagelog = $"⑥缓存组别下所有未占用的库位:{string.Join(",", GetGroupOccupyNot.Select(x => x.StorageNum.ToString()))}";
                //_logger.LogInformation(//messagelog);

                // 这组已经没有库位可以用
                if (GetGroupOccupyNot.Count == 0)
                {
                    // 将缓存保存
                    _cacheServices.Set(cachekey, null);
                    //messagelog = $"⑦缓存组别下已没有库位";
                    //_logger.LogInformation(//messagelog);
                    goto getgroup;
                }

                // 依次判断符合条件的数据
                // 当前组别最后一个被占用的库位编号
                int lastOccupyNum = allStorageOccupy.Last().StorageNum.ToInt();

                for (int j = 0; j < GetGroupOccupyNot.Count; j++)
                {
                    if (GetGroupOccupyNot[j].StorageNum.ToInt() < lastOccupyNum)
                    {
                        // 将缓存保存
                        _cacheServices.Set(cachekey, GetGroupOccupyNot[0].StorageGroup);
                        //messagelog = $"⑧通过缓存推荐库位,推荐的库位:{GetGroupOccupyNot[j].StorageNum}";
                        //_logger.LogInformation(//messagelog);
                        return GetGroupOccupyNot[j].StorageNum;
                    }
                }
            }
        }
    #endregion

    getgroup:
        if (getmateriel.MaterielType.Contains("大料框"))
        {
            var levelmateriel = _MateielLevel.GetFirst(x => x.MaterielLevelNum == getmateriel.MaterielLevelNum);
            // 查询出这个物料在大料框区域有没有存在该物料的区域
            var regionMateriel = _Region.AsQueryable()
                   .Where(x => x.RegionMaterielNum == MaterielNum)
                   .Where(x => !x.RegionNum.Contains("RegionTM"))
                   .ToList();
            if (regionMateriel.Count == 0)
            {
                // 区域编号
                string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("Region");
                // 查询仓库
                var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
                // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
                Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
                {
                    RegionNum = regionnum,
                    RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "大区域",
                    RegionMaterielNum = getmateriel.MaterielNum,
                    RegionStatus = 0,
                    WHNum = wHouse.WHNum,
                    RegionRemake = "大料框区域",
                    CreateTime = DateTime.Now,
                };
                _Region.Insert(regionData);
                //messagelog = $"⑨创建了新区域：{regionnum}";
                //_logger.LogInformation(//messagelog);

                #region SQL语句
                // SELECT DISTINCT(StorageGroup) FROM eg_wms_storage
                // WHERE StorageOccupy = 0 AND StorageRemake LIKE '大料框%'
                // AND StorageGroup NOT IN(
                // SELECT DISTINCT(StorageGroup) FROM eg_wms_storage
                // WHERE StorageOccupy = 1 AND StorageRemake LIKE '大料框%')
                #endregion
                List<string> storageNotOccupyData = new List<string>();
                if (levelmateriel.MaterielLevelName == "常用" || levelmateriel.MaterielLevelName == "次常用")
                {
                    // 根据组号查询所有大料框未占用库位组号
                    storageNotOccupyData = _Storage.AsQueryable()
                                     .Where(x => x.StorageOccupy == 0)
                                     .Where(x => x.StorageName.Contains("大料框DLK"))
                                     .Where(
                                              x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                            .GroupBy(it => it.StorageGroup)
                                                                            .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                            .Select(it => it.StorageGroup)
                                           )
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Distinct()
                                     .Select(x => x.StorageGroup)
                                     .ToList();
                }
                else
                {
                    // 根据组号查询所有大料框未占用库位组号
                    storageNotOccupyData = _Storage.AsQueryable()
                                     .Where(x => x.StorageOccupy == 0)
                                     .Where(x => x.StorageName.Contains("大料框DLK"))
                                     .Where(
                                              x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                            .GroupBy(it => it.StorageGroup)
                                                                            .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                            .Select(it => it.StorageGroup)
                                           )
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Distinct()
                                     .Select(x => x.StorageGroup)
                                     .ToList();
                }

                //messagelog = $"⑩大料框所有未占用库位组号：{string.Join(",", storageNotOccupyData.Select(x => x.ToString()))}";
                //_logger.LogInformation(//messagelog);
                // 将这组的区域编号全部修改为刚刚新增的区域编号
                // 查询出本组的所有库位编号
                var allStorage = _Storage.AsQueryable()
                          .Where(x => x.StorageGroup == storageNotOccupyData[0].ToString())
                          .OrderBy(x => x.StorageNum, OrderByType.Desc)
                          .Select(x => x.StorageNum)
                          .ToList();

                // 修改本组的区域编号
                for (int i = 0; i < allStorage.Count; i++)
                {
                    _Storage.AsUpdateable()
                            .SetColumns(it => new Entity.EG_WMS_Storage()
                            {
                                RegionNum = regionnum,
                                UpdateTime = DateTime.Now,
                            })
                            .Where(x => x.StorageNum == allStorage[i].ToString())
                            .ExecuteCommand();
                }
                //messagelog = $"⑪已修改库位区域编号,组别为:{storageNotOccupyData[0]}";
                //_logger.LogInformation(//messagelog);
                // 修改完成后返回本组中库位编号最大的那个
                // 将缓存保存
                _cacheServices.Set(cachekey, storageNotOccupyData[0].ToString());
                //messagelog = $"⑫推荐的库位:{allStorage[0]}";
                //_logger.LogInformation(//messagelog);
                return allStorage[0].ToString();
            }
            else
            {
                // 查询这个区域下有没有库位
                var storageIsExist = _Storage.AsQueryable()
                         .Where(x => x.RegionNum == regionMateriel[0].RegionNum)
                         .ToList();
                // 没有库位，给这个区域添加库位，并且返回
                if (storageIsExist.Count == 0)
                {
                    string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                    var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                    _cacheServices.Set(cachekey, group.StorageGroup);
                    //messagelog = $"⑬已向区域添加库位,组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                    //_logger.LogInformation(//messagelog);
                    return storagenum;
                }
                string RecommendStorage = null;
                // 有 则调用另一个接口推荐出当前这个物料最合适的库位
                if (getmateriel.MiniTime == null || getmateriel.MiniTime == 0)
                {
                    // 空框的没有在库时间
                    RecommendStorage = AGVCacheStrategyReturnRecommEndStorage(MaterielNum);
                    //messagelog = $"⑭向其他策略请求库位：{RecommendStorage},使用的策略为：AGVCacheStrategyReturnRecommEndStorage";
                    //_logger.LogInformation(//messagelog);
                }
                else
                {
                    // 判断在库时间
                    RecommendStorage = AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage(MaterielNum);
                    //messagelog = $"⑭向其他策略请求库位：{RecommendStorage},使用的策略为：AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage";
                    //_logger.LogInformation(//messagelog);
                }
                if (RecommendStorage.Contains("没有合适的库位"))
                {
                    // 需要分类讨论
                    // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
                    var StoragePreoccupation = _Storage.AsQueryable()
                             .Where(x => x.RegionNum == regionMateriel[0].RegionNum && x.StorageOccupy == 2)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Distinct()
                             .Select(x => x.StorageGroup)
                             .ToList();

                    if (StoragePreoccupation.Count > 0)
                    {
                        // 1.组上的库位被预占用
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组其他未占用库位
                            var StorageUnoccupied = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
                            if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
                            {
                                //messagelog = $"⑮等待预占任务完成后再次请求";
                                //_logger.LogInformation(//messagelog);
                                return "请等待预占任务完成后再次请求！";
                            }
                            else
                            {
                                continue;
                            }
                        }
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 处理正在进行任务的是本组最后一个库位
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组所有库位
                            var AllGroupStorage = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 占用的库位是本组最后一个
                            if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
                            {
                                string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                                var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                                _cacheServices.Set(cachekey, group.StorageGroup);
                                //messagelog = $"⑯占用的库位是本组最后一个，已向区域添加库位，组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                                //_logger.LogInformation(//messagelog);
                                return storagenum;
                            }
                            else
                            {
                                continue;
                            }
                        }
                        return "错误！";
                    }
                    else
                    {
                        // 2.组上已经没有可以用的库位
                        string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                        var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                        _cacheServices.Set(cachekey, group.StorageGroup);
                        //messagelog = $"⑰组上已经没有可以用的库位，已向区域添加库位，组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                        //_logger.LogInformation(//messagelog);
                        return storagenum;
                    }
                }
                else if (RecommendStorage.Contains("等待任务完成"))
                {
                    return "等待任务完成！";
                }
                else
                {
                    var group = _Storage.GetFirst(x => x.StorageNum == RecommendStorage);
                    _cacheServices.Set(cachekey, group.StorageGroup);
                    //messagelog = $"⑱推荐的库位为:{RecommendStorage}";
                    //_logger.LogInformation(//messagelog);
                    return RecommendStorage;
                }
            }
        }
        else
        {
            // 查询出这个物料在小料框区域有没有存在该物料的区域
            var regionMateriel = _Region.GetFirst(x => x.RegionMaterielNum == MaterielNum);
            if (regionMateriel == null)
            {
                // 区域编号
                string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("Region");
                // 查询仓库
                var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
                // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
                Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
                {
                    RegionNum = regionnum,
                    RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "大区域",
                    RegionMaterielNum = getmateriel.MaterielNum,
                    RegionStatus = 0,
                    WHNum = wHouse.WHNum,
                    RegionRemake = "小料框区域",
                    CreateTime = DateTime.Now,
                };
                //messagelog = $"⑨创建了新区域：{regionnum}";
                //_logger.LogInformation(//messagelog);
                _Region.Insert(regionData);
                string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                // 将缓存保存
                _cacheServices.Set(cachekey, group.StorageGroup);
                //messagelog = $"⑫推荐的库位:{storagenum}";
                //_logger.LogInformation(//messagelog);
                return storagenum;
            }
            else
            {
                // 查询这个区域下有没有库位
                var storageIsExist = _Storage.AsQueryable()
                         .Where(x => x.RegionNum == regionMateriel.RegionNum)
                         .ToList();
                // 没有库位，给这个区域添加库位，并且返回
                if (storageIsExist.Count == 0)
                {
                    string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                    var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                    // 将缓存保存
                    _cacheServices.Set(cachekey, group.StorageGroup);
                    //messagelog = $"⑬已向区域添加库位,组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                    //_logger.LogInformation(//messagelog);
                    return storagenum;
                }
                string RecommendStorage = null;
                // 有 则调用另一个接口推荐出当前这个物料最合适的库位
                if (getmateriel.MiniTime == null || getmateriel.MiniTime == 0)
                {
                    // 空框的没有在库时间
                    RecommendStorage = AGVCacheStrategyReturnRecommEndStorage(MaterielNum);
                    //messagelog = $"⑭向其他策略请求库位：{RecommendStorage},使用的策略为：AGVCacheStrategyReturnRecommEndStorage";
                    //_logger.LogInformation(//messagelog);
                }
                else
                {
                    RecommendStorage = AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage(MaterielNum);
                    //messagelog = $"⑭向其他策略请求库位：{RecommendStorage},使用的策略为：AGVCacheStrategyReturnRecommMinimumOccupancyEndStorage";
                    //_logger.LogInformation(//messagelog);
                }
                if (RecommendStorage.Contains("没有合适的库位"))
                {
                    // 需要分类讨论
                    // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
                    var StoragePreoccupation = _Storage.AsQueryable()
                             .Where(x => x.RegionNum == regionMateriel.RegionNum && x.StorageOccupy == 2)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Distinct()
                             .Select(x => x.StorageGroup)
                             .ToList();

                    if (StoragePreoccupation.Count > 0)
                    {
                        // 1.组上的库位被预占用
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组其他未占用库位
                            var StorageUnoccupied = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Select(x => x.StorageNum)
                                     .ToList();


                            // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
                            if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
                            {
                                //messagelog = $"⑮等待预占任务完成后再次请求";
                                //_logger.LogInformation(//messagelog);
                                return "请等待预占任务完成后再次请求！";
                            }
                            else
                            {
                                continue;
                            }
                        }
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 处理正在进行任务的是本组最后一个库位
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组所有库位
                            var AllGroupStorage = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 占用的库位是本组最后一个
                            if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
                            {
                                string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                                var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                                // 将缓存保存
                                _cacheServices.Set(cachekey, group.StorageGroup);
                                //messagelog = $"⑯占用的库位是本组最后一个，已向区域添加库位，组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                                //_logger.LogInformation(//messagelog);
                                return storagenum;
                            }
                            else
                            {
                                continue;
                            }
                        }
                        return "错误！";
                    }
                    else
                    {
                        // 2.组上已经没有可以用的库位
                        string storagenum = UpdateThisGroupRegionNum(MaterielNum);
                        var group = _Storage.GetFirst(x => x.StorageNum == storagenum);
                        // 将缓存保存
                        _cacheServices.Set(cachekey, group.StorageGroup);
                        //messagelog = $"⑰组上已经没有可以用的库位，已向区域添加库位，组别为:{group.StorageGroup},推荐的库位为:{storagenum}";
                        //_logger.LogInformation(//messagelog);
                        return storagenum;
                    }
                }
                else if (RecommendStorage.Contains("等待任务完成！"))
                {
                    return "等待任务完成！";
                }
                else
                {
                    var group = _Storage.GetFirst(x => x.StorageNum == RecommendStorage);
                    // 将缓存保存
                    _cacheServices.Set(cachekey, group.StorageGroup);
                    //messagelog = $"⑱推荐的库位为:{RecommendStorage}";
                    //_logger.LogInformation(//messagelog);
                    return RecommendStorage;
                }
            }
        }
    }

    #endregion

    #region （策略）入库WMS自动推荐的库位（动态转换库位）《无缓存》

    /// <summary>
    /// （策略）入库WMS自动推荐的库位（动态转换库位）《无缓存》
    /// </summary>
    /// <param name="MaterielNum"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVAniseedFrameDynamicSwitchingEndStorage")]
    public string AGVAniseedFrameDynamicSwitchingEndStorage(string MaterielNum)
    {
        // 查询这个物料是那种类型的物料（常用还是不常用）
        var getmateriel = _Materiel.GetFirst(x => x.MaterielNum == MaterielNum);
        //string logmessage = "";
        if (getmateriel.MaterielType.Contains("大料框"))
        {
            var levelmateriel = _MateielLevel.GetFirst(x => x.MaterielLevelNum == getmateriel.MaterielLevelNum);
            // 查询出这个物料在大料框区域有没有存在该物料的区域
            var regionMateriel = _Region.AsQueryable()
                       .Where(x => x.RegionMaterielNum == MaterielNum)
                       .Where(x => !x.RegionNum.Contains("RegionTM"))
                       .ToList();
            if (regionMateriel.Count == 0)
            {
                // 区域编号
                string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("Region");
                // 查询仓库
                var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
                // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
                Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
                {
                    RegionNum = regionnum,
                    RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "大区域",
                    RegionMaterielNum = getmateriel.MaterielNum,
                    RegionStatus = 0,
                    WHNum = wHouse.WHNum,
                    RegionRemake = "大料框区域",
                    CreateTime = DateTime.Now,
                };

                //logmessage = $"①添加了新区域：{regionnum}";
                //_logger.LogInformation(//logmessage);
                _Region.Insert(regionData);

                #region SQL语句
                // SELECT DISTINCT(StorageGroup) FROM eg_wms_storage
                // WHERE StorageOccupy = 0 AND StorageRemake LIKE '大料框%'
                // AND StorageGroup NOT IN(
                // SELECT DISTINCT(StorageGroup) FROM eg_wms_storage
                // WHERE StorageOccupy = 1 AND StorageRemake LIKE '大料框%')
                #endregion
                List<string> storageNotOccupyData = new List<string>();
                if (levelmateriel.MaterielLevelName == "常用" || levelmateriel.MaterielLevelName == "次常用")
                {
                    // 根据组号查询所有大料框未占用库位组号
                    storageNotOccupyData = _Storage.AsQueryable()
                                     .Where(x => x.StorageOccupy == 0)
                                     .Where(x => x.StorageName.Contains("大料框DLK"))
                                     .Where(
                                              x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                            .GroupBy(it => it.StorageGroup)
                                                                            .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                            .Select(it => it.StorageGroup)
                                           )
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Distinct()
                                     .Select(x => x.StorageGroup)
                                     .ToList();
                }
                else
                {
                    // 根据组号查询所有大料框未占用库位组号
                    storageNotOccupyData = _Storage.AsQueryable()
                                     .Where(x => x.StorageOccupy == 0)
                                     .Where(x => x.StorageName.Contains("大料框DLK"))
                                     .Where(
                                              x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                            .GroupBy(it => it.StorageGroup)
                                                                            .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                            .Select(it => it.StorageGroup)
                                           )
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Distinct()
                                     .Select(x => x.StorageGroup)
                                     .ToList();
                }
                //logmessage = $"②本次查询得到大料框所有未占用库位组号：{string.Join(",", storageNotOccupyData.Select(x => x.ToString()))}";
                //_logger.LogInformation(//logmessage);

                // 将这组的区域编号全部修改为刚刚新增的区域编号
                // 查询出本组的所有库位编号
                var allStorage = _Storage.AsQueryable()
                          .Where(x => x.StorageGroup == storageNotOccupyData[0].ToString())
                          .OrderBy(x => x.StorageNum, OrderByType.Desc)
                          .Select(x => x.StorageNum)
                          .ToList();

                // 修改本组的区域编号
                for (int i = 0; i < allStorage.Count; i++)
                {
                    _Storage.AsUpdateable()
                            .SetColumns(it => new Entity.EG_WMS_Storage()
                            {
                                RegionNum = regionnum,
                                UpdateTime = DateTime.Now,
                            })
                            .Where(x => x.StorageNum == allStorage[i].ToString())
                            .ExecuteCommand();
                }

                //logmessage = $"③已修改区域编号，组号为：{storageNotOccupyData[0].ToString()}，推荐的库位为：{allStorage[0].ToString()}";
                //_logger.LogInformation(//logmessage);
                // 修改完成后返回本组中库位编号最大的那个
                return allStorage[0].ToString();
            }
            else
            {
                // 查询这个区域下有没有库位
                var storageIsExist = _Storage.AsQueryable()
                         .Where(x => x.RegionNum == regionMateriel[0].RegionNum)
                         .ToList();
                // 没有库位，给这个区域添加库位，并且返回
                if (storageIsExist.Count == 0)
                {
                    return UpdateThisGroupRegionNum(MaterielNum);
                }
                string RecommendStorage = null;
                // 有 则调用另一个接口推荐出当前这个物料最合适的库位
                if (getmateriel.MiniTime == null || getmateriel.MiniTime == 0)
                {
                    // 空框的没有在库时间
                    RecommendStorage = AGVStrategyReturnRecommEndStorage(MaterielNum);
                    //logmessage = $"④向其他策略请求库位：{RecommendStorage}，使用的策略为：AGVStrategyReturnRecommEndStorage";
                    //_logger.LogInformation(//logmessage);
                }
                else
                {
                    // 判断在库时间
                    RecommendStorage = AGVStrategyReturnRecommMinimumOccupancyEndStorage(MaterielNum);
                    //logmessage = $"④向其他策略请求库位：{RecommendStorage}，使用的策略为：AGVStrategyReturnRecommMinimumOccupancyEndStorage";
                    //_logger.LogInformation(//logmessage);
                }

                if (RecommendStorage.Contains("没有合适"))
                {
                    // 需要分类讨论
                    // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
                    var StoragePreoccupation = _Storage.AsQueryable()
                             .Where(x => x.RegionNum == regionMateriel[0].RegionNum && x.StorageOccupy == 2)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Distinct()
                             .Select(x => x.StorageGroup)
                             .ToList();

                    if (StoragePreoccupation.Count > 0)
                    {
                        // 1.组上的库位被预占用
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组其他未占用库位
                            var StorageUnoccupied = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Select(x => x.StorageNum)
                                     .ToList();


                            // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
                            if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
                            {
                                //logmessage = $"⑤等待预占任务完成后再次请求";
                                //_logger.LogInformation(//logmessage);
                                return "请等待预占任务完成后再次请求！";
                            }
                            else
                            {
                                continue;
                            }
                        }
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 处理正在进行任务的是本组最后一个库位
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组所有库位
                            var AllGroupStorage = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 占用的库位是本组最后一个
                            if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
                            {
                                return UpdateThisGroupRegionNum(MaterielNum);
                            }
                            else
                            {
                                continue;
                            }
                        }
                        return "错误！";
                    }
                    else
                    {
                        // 2.组上已经没有可以用的库位
                        return UpdateThisGroupRegionNum(MaterielNum);
                    }
                }
                else
                {
                    //logmessage = $"⑥推荐的库位为:{RecommendStorage}";
                    //_logger.LogInformation(//logmessage);
                    return RecommendStorage;
                }
            }
        }
        else
        {
            // 查询出这个物料在小料框区域有没有存在该物料的区域
            var regionMateriel = _Region.GetFirst(x => x.RegionMaterielNum == MaterielNum);
            if (regionMateriel == null)
            {
                // 区域编号
                string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("Region");
                // 查询仓库
                var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
                // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
                Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
                {
                    RegionNum = regionnum,
                    RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "大区域",
                    RegionMaterielNum = getmateriel.MaterielNum,
                    RegionStatus = 0,
                    WHNum = wHouse.WHNum,
                    RegionRemake = "小料框区域",
                    CreateTime = DateTime.Now,
                };
                //logmessage = $"①添加了新区域：{regionnum}";
                //_logger.LogInformation(//logmessage);
                _Region.Insert(regionData);
                return UpdateThisGroupRegionNum(MaterielNum);
            }
            else
            {
                // 查询这个区域下有没有库位
                var storageIsExist = _Storage.AsQueryable()
                         .Where(x => x.RegionNum == regionMateriel.RegionNum)
                         .ToList();
                // 没有库位，给这个区域添加库位，并且返回
                if (storageIsExist.Count == 0)
                {
                    return UpdateThisGroupRegionNum(MaterielNum);
                }
                string RecommendStorage = null;
                // 有 则调用另一个接口推荐出当前这个物料最合适的库位
                if (getmateriel.MiniTime == null || getmateriel.MiniTime == 0)
                {
                    // 空框的没有在库时间
                    RecommendStorage = AGVStrategyReturnRecommEndStorage(MaterielNum);
                    //logmessage = $"④向其他策略请求库位：{RecommendStorage}，使用的策略为：AGVStrategyReturnRecommEndStorage";
                    //_logger.LogInformation(//logmessage);
                }
                else
                {
                    RecommendStorage = AGVStrategyReturnRecommMinimumOccupancyEndStorage(MaterielNum);
                    //logmessage = $"④向其他策略请求库位：{RecommendStorage}，使用的策略为：AGVStrategyReturnRecommMinimumOccupancyEndStorage";
                    //_logger.LogInformation(//logmessage);
                }
                if (RecommendStorage.Contains("没有合适"))
                {
                    // 需要分类讨论
                    // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
                    var StoragePreoccupation = _Storage.AsQueryable()
                             .Where(x => x.RegionNum == regionMateriel.RegionNum && x.StorageOccupy == 2)
                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
                             .Distinct()
                             .Select(x => x.StorageGroup)
                             .ToList();

                    if (StoragePreoccupation.Count > 0)
                    {
                        // 1.组上的库位被预占用
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组其他未占用库位
                            var StorageUnoccupied = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .Select(x => x.StorageNum)
                                     .ToList();


                            // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
                            if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
                            {
                                //logmessage = $"⑤等待预占任务完成后再次请求";
                                //_logger.LogInformation(//logmessage);
                                return "请等待预占任务完成后再次请求！";
                            }
                            else
                            {
                                continue;
                            }
                        }
                        for (int i = 0; i < StoragePreoccupation.Count; i++)
                        {
                            // 处理正在进行任务的是本组最后一个库位
                            // 查询本组预占用的库位
                            var StorageBeginOccupy = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 查询本组所有库位
                            var AllGroupStorage = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
                                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                     .Select(x => x.StorageNum)
                                     .ToList();

                            // 占用的库位是本组最后一个
                            if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
                            {
                                return UpdateThisGroupRegionNum(MaterielNum);
                            }
                            else
                            {
                                continue;
                            }
                        }
                        return "错误！";
                    }
                    else
                    {
                        // 2.组上已经没有可以用的库位
                        return UpdateThisGroupRegionNum(MaterielNum);
                    }
                }
                else
                {
                    //logmessage = $"⑥推荐的库位为:{RecommendStorage}";
                    //_logger.LogInformation(//logmessage);
                    return RecommendStorage;
                }
            }
        }
    }

    #endregion

    #region （策略）尾料入库WMS自动推荐的库位（动态转换库位）

    /// <summary>
    /// （策略）尾料入库WMS自动推荐的库位（动态转换库位）
    /// </summary>
    /// <param name="MaterielNum"></param>
    /// <returns></returns>
    [HttpPost]
    [AllowAnonymous]
    [ApiDescriptionSettings(Name = "AGVTailMaterialAniseedFrameDynamicSwitchingEndStorage")]
    public string AGVTailMaterialAniseedFrameDynamicSwitchingEndStorage(string MaterielNum)
    {
        // 查询这个物料是那种类型的物料
        var getmateriel = _Materiel.GetFirst(x => x.MaterielNum == MaterielNum);
        //string logmessage = "";
        // 查询出这个物料在大料框尾料区域有没有存在该物料的区域
        var regionMateriel = _Region.AsQueryable()
                                    .Where(x => x.RegionNum.Contains("RegionTM"))
                                    .Where(x => x.RegionMaterielNum == MaterielNum)
                                    .Distinct()
                                    .ToList();

        // 没有这个区域
        if (regionMateriel.Count == 0)
        {
            // 区域编号
            string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("RegionTM");
            // 查询仓库
            var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
            // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
            Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
            {
                RegionNum = regionnum,
                RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "尾料区域",
                RegionMaterielNum = getmateriel.MaterielNum,
                RegionStatus = 0,
                WHNum = wHouse.WHNum,
                RegionRemake = "大料框尾料区域",
                CreateTime = DateTime.Now,
            };
            //logmessage = $"①添加了新区域：{regionnum}";
            //_logger.LogInformation(//logmessage);
            _Region.Insert(regionData);
            return UpdateTailMaterialThisGroupRegionNum(MaterielNum);
        }
        else
        {
            // 查询这个区域下有没有库位
            var storageIsExist = _Storage.AsQueryable()
                     .Where(x => x.RegionNum == regionMateriel[0].RegionNum)
                     .ToList();
            // 没有库位，给这个区域添加库位，并且返回
            if (storageIsExist.Count == 0)
            {
                return UpdateTailMaterialThisGroupRegionNum(MaterielNum);
            }
            // 有 则调用另一个接口推荐出当前这个物料最合适的库位
            string RecommendStorage = AGVTailMaterialStrategyReturnRecommEndStorage(MaterielNum);
            if (RecommendStorage.Contains("没有合适"))
            {
                // 需要分类讨论
                // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
                var StoragePreoccupation = _Storage.AsQueryable()
                         .Where(x => x.RegionNum == regionMateriel[0].RegionNum && x.StorageOccupy == 2)
                         .OrderBy(x => x.StorageNum, OrderByType.Asc)
                         .Distinct()
                         .Select(x => x.StorageGroup)
                         .ToList();

                if (StoragePreoccupation.Count > 0)
                {
                    // 1.组上的库位被预占用
                    for (int i = 0; i < StoragePreoccupation.Count; i++)
                    {
                        // 查询本组预占用的库位
                        var StorageBeginOccupy = _Storage.AsQueryable()
                                 .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                 .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                 .Select(x => x.StorageNum)
                                 .ToList();

                        // 查询本组其他未占用库位
                        var StorageUnoccupied = _Storage.AsQueryable()
                                 .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
                                 .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                 .Select(x => x.StorageNum)
                                 .ToList();


                        // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
                        if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
                        {
                            return "请等待预占任务完成后再次请求！";
                        }
                        else
                        {
                            continue;
                        }
                    }
                    for (int i = 0; i < StoragePreoccupation.Count; i++)
                    {
                        // 处理正在进行任务的是本组最后一个库位
                        // 查询本组预占用的库位
                        var StorageBeginOccupy = _Storage.AsQueryable()
                                 .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
                                 .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                 .Select(x => x.StorageNum)
                                 .ToList();

                        // 查询本组所有库位
                        var AllGroupStorage = _Storage.AsQueryable()
                                 .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
                                 .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                 .Select(x => x.StorageNum)
                                 .ToList();

                        // 占用的库位是本组最后一个
                        if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
                        {
                            return UpdateTailMaterialThisGroupRegionNum(MaterielNum);
                        }
                        else
                        {
                            continue;
                        }
                    }
                    return "错误！";
                }
                else
                {
                    // 2.组上已经没有可以用的库位
                    return UpdateTailMaterialThisGroupRegionNum(MaterielNum);
                }
            }
            else
            {
                //logmessage = $"②推荐的库位为：{RecommendStorage}";
                //_logger.LogInformation(//logmessage);
                return RecommendStorage;
            }
        }

    }

    #endregion

    /// <summary>
    /// 修改本组区域编号
    /// </summary>
    /// <param name="MaterielNum">物料编号</param>
    /// <returns>库位编号</returns>
    [ApiDescriptionSettings(true)]
    public string UpdateThisGroupRegionNum(string MaterielNum)
    {
        //string logmessage = "";
        // 查询这个物料是那种类型的物料（常用还是不常用）
        var getmateriel = _Materiel.GetFirst(x => x.MaterielNum == MaterielNum);
        if (getmateriel.MaterielType.Contains("大料框"))
        {
            // 查询得到这个大料框物料属于哪个区域
            var regionData = _Region.AsQueryable()
                   .Where(x => x.RegionMaterielNum == MaterielNum)
                   .Where(x => !x.RegionNum.Contains("RegionTM"))
                   .ToList();
            //logmessage = $"①本次请求物料的区域为：{regionData[0].RegionNum}";
            //_logger.LogInformation(//logmessage);
            var levelmateriel = _MateielLevel.GetFirst(x => x.MaterielLevelNum == getmateriel.MaterielLevelNum);
            List<string> storageNotOccupyData = new List<string>();
            if (levelmateriel.MaterielLevelName == "常用" || levelmateriel.MaterielLevelName == "次常用")
            {
                // 根据组号查询所有大料框未占用库位组号
                storageNotOccupyData = _Storage.AsQueryable()
                                 .Where(x => x.StorageOccupy == 0)
                                 .Where(x => x.StorageName.Contains("大料框DLK"))
                                 .Where(
                                          x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                        .GroupBy(it => it.StorageGroup)
                                                                        .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                        .Select(it => it.StorageGroup)
                                       )
                                 .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                 .Distinct()
                                 .Select(x => x.StorageGroup)
                                 .ToList();
            }
            else
            {
                // 根据组号查询所有大料框未占用库位组号
                storageNotOccupyData = _Storage.AsQueryable()
                                 .Where(x => x.StorageOccupy == 0)
                                 .Where(x => x.StorageName.Contains("大料框DLK"))
                                 .Where(
                                          x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                        .GroupBy(it => it.StorageGroup)
                                                                        .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框DLK"))
                                                                        .Select(it => it.StorageGroup)
                                       )
                                 .OrderBy(x => x.StorageNum, OrderByType.Asc)
                                 .Distinct()
                                 .Select(x => x.StorageGroup)
                                 .ToList();
            }

            //logmessage = $"②大料框所有未占用的组号：{string.Join(",", storageNotOccupyData.Select(x => x.ToString()))}";
            //_logger.LogInformation(//logmessage);

            // 查询出本组的所有库位编号
            var allStorage = _Storage.AsQueryable()
                     .Where(x => x.StorageGroup == storageNotOccupyData[0].ToString())
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => x.StorageNum)
                     .ToList();

            // 修改本组的区域编号
            for (int j = 0; j < allStorage.Count; j++)
            {
                _Storage.AsUpdateable()
                        .SetColumns(it => new Entity.EG_WMS_Storage()
                        {
                            RegionNum = regionData[0].RegionNum,
                            UpdateTime = DateTime.Now,
                        })
                        .Where(x => x.StorageNum == allStorage[j].ToString())
                        .ExecuteCommand();
            }
            //logmessage = $"③已将本组：{storageNotOccupyData[0].ToString()}，中所有的库位添加到当前区域，推荐的库位为：{allStorage[0].ToString()}";
            //_logger.LogInformation(//logmessage);
            // 修改完成后返回本组中库位编号最大的那个
            return allStorage[0].ToString();
        }
        else
        {
            // 查询得到这个小料框物料属于哪个区域
            var regionData = _Region.GetFirst(x => x.RegionMaterielNum == MaterielNum);
            //logmessage = $"①本次请求物料的区域为：{regionData.RegionNum}";
            //_logger.LogInformation(//logmessage);
            List<string> storageNotOccupyData = new List<string>();
            // 根据组号查询所有小料框未占用库位组号
            storageNotOccupyData = _Storage.AsQueryable()
                             .Where(x => x.StorageOccupy == 0)
                             .Where(x => x.StorageName.Contains("小料框空框PBT以及机芯共用"))
                             .Where(
                                      x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                    .GroupBy(it => it.StorageGroup)
                                                                    .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("小料框空框PBT以及机芯共用"))
                                                                    .Select(it => it.StorageGroup)
                                   )
                             .OrderBy(x => x.StorageNum, OrderByType.Desc)
                             .Distinct()
                             .Select(x => x.StorageGroup)
                             .ToList();

            //logmessage = $"②小料框所有未占用的组号：{string.Join(",", storageNotOccupyData.Select(x => x.ToString()))}";
            //_logger.LogInformation(//logmessage);

            // 查询出本组的所有库位编号
            var allStorage = _Storage.AsQueryable()
                     .Where(x => x.StorageGroup == storageNotOccupyData[0].ToString())
                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                     .Select(x => x.StorageNum)
                     .ToList();

            // 修改本组的区域编号
            for (int j = 0; j < allStorage.Count; j++)
            {
                _Storage.AsUpdateable()
                        .SetColumns(it => new Entity.EG_WMS_Storage()
                        {
                            RegionNum = regionData.RegionNum,
                            UpdateTime = DateTime.Now,
                        })
                        .Where(x => x.StorageNum == allStorage[j].ToString())
                        .ExecuteCommand();
            }

            //logmessage = $"③已将本组：{storageNotOccupyData[0].ToString()}，中所有的库位添加到当前区域，推荐的库位为：{allStorage[0].ToString()}";
            //_logger.LogInformation(//logmessage);
            // 修改完成后返回本组中库位编号最大的那个
            return allStorage[0].ToString();
        }
    }

    /// <summary>
    /// 修改尾料的本组区域编号
    /// </summary>
    /// <param name="MaterielNum">物料编号</param>
    /// <returns>库位编号</returns>
    [ApiDescriptionSettings(true)]
    public string UpdateTailMaterialThisGroupRegionNum(string MaterielNum)
    {
        //string logmessage = "";
        // 查询出这个物料在大料框尾料区域有没有存在该物料的区域
        var regionMateriel = _Region.AsQueryable()
                                    .Where(x => x.RegionNum.Contains(("RegionTM")))
                                    .Where(x => x.RegionMaterielNum == MaterielNum)
                                    .ToList();
        List<string> storageNotOccupyData = new List<string>();
        //logmessage = $"①本次请求物料的区域为：{regionMateriel[0].RegionNum}";
        //_logger.LogInformation(//logmessage);
        // 根据组号查询所有大料框尾料未占用库位组号
        storageNotOccupyData = _Storage.AsQueryable()
                         .Where(x => x.StorageOccupy == 0)
                         .Where(x => x.StorageName.Contains("大料框尾料WL"))
                         .Where(
                                  x => x.StorageGroup != SqlFunc.Subqueryable<Entity.EG_WMS_Storage>()
                                                                .GroupBy(it => it.StorageGroup)
                                                                .Where(it => (it.StorageOccupy == 1 || it.StorageOccupy == 2) && it.StorageName.Contains("大料框尾料WL"))
                                                                .Select(it => it.StorageGroup)
                               )
                         .OrderBy(x => x.StorageNum, OrderByType.Desc)
                         .Distinct()
                         .Select(x => x.StorageGroup)
                         .ToList();

        //logmessage = $"②查询出大料框尾料的所有未占用组别：{string.Join(",", storageNotOccupyData.Select(x => x.ToString()))}";
        //_logger.LogInformation(//logmessage);
        // 尾料区域没有库位了
        if (storageNotOccupyData.Count == 0)
        {
            throw Oops.Oh("尾料库位已满！");
        }

        // 查询出本组的所有库位编号 
        var allStorage = _Storage.AsQueryable()
                 .Where(x => x.StorageGroup == storageNotOccupyData[0].ToString())
                 .OrderBy(x => x.StorageNum, OrderByType.Desc)
                 .Select(x => x.StorageNum)
                 .ToList();

        // 修改本组的区域编号
        for (int j = 0; j < allStorage.Count; j++)
        {
            _Storage.AsUpdateable()
                    .SetColumns(it => new Entity.EG_WMS_Storage()
                    {
                        RegionNum = regionMateriel[0].RegionNum,
                        UpdateTime = DateTime.Now,
                    })
                    .Where(x => x.StorageNum == allStorage[j].ToString())
                    .ExecuteCommand();
        }
        //logmessage = $"③已将本组库位添加到该区域，组号为：{storageNotOccupyData[0].ToString()}，推荐的库位为：{allStorage[0].ToString()}";
        // 修改完成后返回本组中库位编号最大的那个
        return allStorage[0].ToString();
    }

    /// <summary>
    /// 得到物料可以出库的组别
    /// </summary>
    /// <returns></returns>
    [ApiDescriptionSettings(true)]
    public List<string> GetOutBoundStorageGroup(string materielNum)
    {
        // 根据物料编号，得到这个物料属于那个区域
        var dataRegion = _Region.AsQueryable().Where(x => x.RegionMaterielNum == materielNum).ToList();
        // 获得这个物料的需在库时间
        var materielData = _Materiel.GetFirst(x => x.MaterielNum == materielNum);
        // 用于保存每个区域里面的数据
        List<string> datagroup = new List<string>();
        if (dataRegion == null || dataRegion.Count == 0)
        {
            throw Oops.Oh("区域未绑定物料");
        }
        for (int k = 0; k < dataRegion.Count; k++)
        {
            // 查询是否有正在进行中的任务库位的组别

            var dataStorageGroup = _Storage.AsQueryable()
                       .Where(a => a.TaskNo != null && a.RegionNum == dataRegion[k].RegionNum)
                       .Distinct()
                       .Select(a => new
                       {
                           a.StorageGroup,
                       })
                       .ToList();

            // 将有任务的组别保存
            string[] strings = new string[dataStorageGroup.Count];
            for (int i = 0; i < dataStorageGroup.Count; i++)
            {
                strings[i] = dataStorageGroup[i].StorageGroup;
            }

            // 查询所有的组别（排除不符合条件的组别）
            var getGroup = _Storage.AsQueryable()
                            .Where(x => !strings.Contains(x.StorageGroup) && x.StorageType == 0 &&
                                   x.StorageGroup != null && x.RegionNum == dataRegion[k].RegionNum)
                            .Distinct()
                            .Select(x => x.StorageGroup)
                            .ToList();

            // 如果这一组的最后一个的时间还没有达到，则这一组都用不了 
            string[] stringss = new string[getGroup.Count];
            var storagenum = new Entity.EG_WMS_Storage();
            for (int i = 0; i < getGroup.Count; i++)
            {
                var notStorageGroup = _Storage.AsQueryable()
                                     .Where(x => x.StorageGroup == getGroup[i] && x.StorageOccupy == 1)
                                     .OrderBy(x => x.StorageNum, OrderByType.Desc)
                                     .ToList();

                if (notStorageGroup.Count != 0)
                {
                    storagenum = notStorageGroup.Last();
                }

                // 这个组的最后一条数据不符合条件，把这个组别保存下来
                if (storagenum.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) > DateTime.Now)
                {
                    stringss[i] = storagenum.StorageGroup.ToString();
                }

            }

            // 查询库位并且排除不符合条件的组别和库位

            var getStorage = _Storage.AsQueryable()
                     .Where(a => a.StorageStatus == 0 && a.StorageGroup != null
                     && a.StorageOccupy == 1 && a.RegionNum == dataRegion[k].RegionNum &&
                     !strings.Contains(a.StorageGroup) && !stringss.Contains(a.StorageGroup) &&
                     a.StorageProductionDate.ToDateTime().AddHours((double)materielData.MiniTime) < DateTime.Now)
                     .OrderBy(a => a.StorageNum, OrderByType.Asc)
                     .Distinct()
                     .Select(a => new
                     {
                         a.StorageGroup,
                     })
                     .ToList();

            foreach (var item in getStorage)
            {
                datagroup.Add(item.StorageGroup);
            }
        }

        return datagroup;
    }

    /// <summary>
    /// 推送库位变化 
    /// </summary>
    [AllowAnonymous]
    public async Task ChangeLocationInformation(ChangeStorageDto model)
    {
        HttpClientHelper httpClientHelper = new HttpClientHelper();
        //string messagelog = "";
        // 查询得到这个库位是那个组别
        var storagedata = _Storage.GetFirst(x => x.StorageNum == model.StorageNum);
        model.StorageGroup = storagedata.StorageGroup;
        var forwardedEndpoint = App.GetOptions<ThirdServiceOptions>().ChangeAPI.ToString();
        if (string.IsNullOrEmpty(forwardedEndpoint))
        {
            return;
        }
        string jsonstr = JsonConvert.SerializeObject(model);
        var response = await httpClientHelper.PostToolAsync(forwardedEndpoint, jsonstr);
        if (response.IsSuccessStatusCode)
        {
            //messagelog = "推送库位变化成功！";
            return;
            //_logger.LogInformation(//messagelog);
        }
        else
        {
            throw Oops.Oh("推送库位变化失败！");
        }

    }
}

//-------------------------------------/归档/-------------------------------------//

#region （策略）（立库）堆高车出库WMS自动推荐的库位（按照先入先出，以及输入物料、物料数量去推荐哪几个库位）

///// <summary>
///// （策略）（立库）堆高车出库WMS自动推荐的库位（按照先入先出，以及输入物料、物料数量去推荐哪几个库位）
///// </summary>
///// <param name="materielnum">物料编号</param>
///// <param name="quantity">出库总数</param>
///// <returns></returns>
//[HttpPost]
//[ApiDescriptionSettings(Name = "AGVStackingHighCarStorageOutBound", Order = 996)]
//public List<string> AGVStackingHighCarStorageOutBound(string materielnum, int quantity)
//{
//    // 根据物料产品筛选物料在哪几个库位上

//    var storagenum = _Storage.AsQueryable()
//            .InnerJoin<EG_WMS_InventoryDetail>((a, b) => a.StorageNum == b.StorageNum)
//            .InnerJoin<EG_WMS_Inventory>((a, b, c) => b.InventoryNum == c.InventoryNum)
//            .Where((a, b, c) => a.StorageType == 1 && a.StorageOccupy == 1 && a.StorageStatus == 0 && c.MaterielNum == materielnum)
//            .OrderBy((a, b, c) => a.UpdateTime, OrderByType.Asc)
//            .Select((a, b, c) => new { a.StorageNum, c.ICountAll })
//            .ToList();

//    if (storagenum == null)
//    {
//        return new List<string> { "当前没有合适的库位！" };
//    }

//    List<string> result = new List<string>();
//    int? sumcount = 0;
//    for (int i = 0; i < storagenum.Count; i++)
//    {
//        // 第一种情况（输入的数量恰好等于或大于第一个库位上的数量）
//        if (storagenum[i].ICountAll >= quantity)
//        {
//            result.Clear();
//            result.Add(storagenum[i].StorageNum.ToString());
//            break;
//        }
//        else if (storagenum[i].ICountAll < quantity)
//        {
//            if (sumcount >= quantity)
//            {
//                break;
//            }
//            sumcount += storagenum[i].ICountAll;
//            result.Add(storagenum[i].StorageNum.ToString());
//        }
//        else if (i == storagenum.Count - 1)
//        {
//            if (sumcount < quantity)
//            {
//                return new List<string> { "当前在库中该物料数量不足出库数量！" };
//            }
//        }
//    }
//    return result;
//}


#endregion

#region （策略）小料框入库空框PBT机芯WMS自动推荐的库位（动态转换库位）
///// <summary>
///// （策略）小料框入库空框PBT机芯在同一库位上（动态转换库位）
///// </summary>
///// <param name="MaterielNum">物料编号</param>
///// <returns></returns>
//[HttpPost]
//[ApiDescriptionSettings(Name = "AGVSmallMaterialFrameSwitchingEndStorage")]
//public string AGVSmallMaterialFrameSwitchingEndStorage(string MaterielNum)
//{
//    // 查询这个物料是那种类型的物料（PBT还是小料框空框）
//    var getmateriel = _Materiel.GetFirst(x => x.MaterielNum == MaterielNum);
//    // 查询出这个物料在小料框区域有没有存在该物料的区域
//    var regionMateriel = _Region.GetFirst(x => x.RegionMaterielNum == MaterielNum);
//    if (regionMateriel == null)
//    {
//        // 区域编号
//        string regionnum = toolTimes.GetTheCurrentTimeTimeStamp("Region");
//        // 查询仓库
//        var wHouse = _WHouse.GetFirst(x => x.WHType == 0);
//        // 没有 则通过新增区域将推荐的这一组库位放入到指定的区域中
//        Entity.EG_WMS_Region regionData = new Entity.EG_WMS_Region()
//        {
//            RegionNum = regionnum,
//            RegionName = getmateriel.MaterielName + getmateriel.MaterielReamke + "大区域",
//            RegionMaterielNum = getmateriel.MaterielNum,
//            RegionStatus = 0,
//            WHNum = wHouse.WHNum,
//            RegionRemake = "小料框区域",
//            CreateTime = DateTime.Now,
//        };
//        _Region.Insert(regionData);
//        return UpdateThisGroupRegionNum(MaterielNum);
//    }
//    else
//    {
//        // 查询这个区域下有没有库位
//        var storageIsExist = _Storage.AsQueryable()
//                 .Where(x => x.RegionNum == regionMateriel.RegionNum)
//                 .ToList();
//        // 没有库位，给这个区域添加库位，并且返回
//        if (storageIsExist.Count == 0)
//        {
//            return UpdateThisGroupRegionNum(MaterielNum);
//        }
//        // 有 则调用另一个接口推荐出当前这个物料最合适的库位
//        string RecommendStorage = AGVStrategyReturnRecommEndStorage(MaterielNum);
//        if (RecommendStorage.Contains("没有合适"))
//        {
//            // 需要分类讨论
//            // 查询出区域下的库位是不是有被预占用的库位导致没有合适的库位
//            var StoragePreoccupation = _Storage.AsQueryable()
//                     .Where(x => x.RegionNum == regionMateriel.RegionNum && x.StorageOccupy == 2)
//                     .OrderBy(x => x.StorageNum, OrderByType.Asc)
//                     .Distinct()
//                     .Select(x => x.StorageGroup)
//                     .ToList();

//            if (StoragePreoccupation.Count > 0)
//            {
//                // 1.组上的库位被预占用
//                for (int i = 0; i < StoragePreoccupation.Count; i++)
//                {
//                    // 查询本组预占用的库位
//                    var StorageBeginOccupy = _Storage.AsQueryable()
//                             .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
//                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
//                             .Select(x => x.StorageNum)
//                             .ToList();

//                    // 查询本组其他未占用库位
//                    var StorageUnoccupied = _Storage.AsQueryable()
//                             .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 0)
//                             .OrderBy(x => x.StorageNum, OrderByType.Desc)
//                             .Select(x => x.StorageNum)
//                             .ToList();


//                    // 未占用的库位的最小的库位编号都满足，代表本组还有库位可以使用，只需要等待预占的任务执行完成
//                    if (StorageBeginOccupy[0].ToInt() > StorageUnoccupied.Min().ToInt() && StorageUnoccupied.Count != 0)
//                    {
//                        return "请等待预占任务完成后再次请求！";
//                    }
//                    else
//                    {
//                        continue;
//                    }
//                }
//                for (int i = 0; i < StoragePreoccupation.Count; i++)
//                {
//                    // 处理正在进行任务的是本组最后一个库位
//                    // 查询本组预占用的库位
//                    var StorageBeginOccupy = _Storage.AsQueryable()
//                             .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString() && x.StorageOccupy == 2)
//                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
//                             .Select(x => x.StorageNum)
//                             .ToList();

//                    // 查询本组所有库位
//                    var AllGroupStorage = _Storage.AsQueryable()
//                             .Where(x => x.StorageGroup == StoragePreoccupation[i].ToString())
//                             .OrderBy(x => x.StorageNum, OrderByType.Asc)
//                             .Select(x => x.StorageNum)
//                             .ToList();

//                    // 占用的库位是本组最后一个
//                    if (StorageBeginOccupy.Min().ToInt() == AllGroupStorage.Min().ToInt())
//                    {
//                        return UpdateThisGroupRegionNum(MaterielNum);
//                    }
//                    else
//                    {
//                        continue;
//                    }
//                }
//                return "错误！";
//            }
//            else
//            {
//                // 2.组上已经没有可以用的库位
//                return UpdateThisGroupRegionNum(MaterielNum);
//            }
//        }
//        else
//        {
//            return RecommendStorage;
//        }
//    }
//}

#endregion

#region （策略）推荐小料框出库缓存库位（山东宇翔）

///// <summary>
///// （策略）推荐小料框出库缓存库位（山东宇翔）
///// </summary>
///// <returns></returns>
//[AllowAnonymous]
//[HttpGet]
//[ApiDescriptionSettings(Name = "AGVStrategySmallFrameOutBoundStorage")]
//public string AGVStrategySmallFrameOutBoundStorage()
//{
//    // 
//    // 查询本组库位内有没有正在进行任务的库位
//    var occupyStorageNum = _Storage.AsQueryable()
//             .Where(x => x.RegionNum == "Region1710296466" && x.StorageOccupy == 2)
//             .OrderBy(x => x.StorageNum, OrderByType.Desc)
//             .ToList();

//    if (occupyStorageNum.Count != 0)
//    {
//        throw Oops.Oh("当前空框缓存库位里有正在进行中的任务，请等待任务执行完成后在次下发任务！");
//    }

//    // 查询出小料框缓存的库位
//    var data = _Storage.AsQueryable()
//                       .Where(x => x.RegionNum == "Region1710296466" && x.StorageOccupy == 1)
//                       .OrderBy(x => x.StorageNum, OrderByType.Asc)
//                       .ToList();

//    if (data.Count == 0)
//    {
//        throw Oops.Oh("当前小料框缓存库位没有可移动的");
//    }

//    string storagenum = data.First().StorageNum.ToString();
//    return storagenum;

//}

#endregion

#region （策略）推荐小料框入库缓存库位（山东宇翔）

///// <summary>
///// （策略）推荐小料框入库缓存库位（山东宇翔）
///// </summary>
///// <returns></returns>
//[HttpGet]
//[AllowAnonymous]
//[ApiDescriptionSettings(Name = "AGVStrategySmallFrameInBoundStorage")]
//public string AGVStrategySmallFrameInBoundStorage()
//{
//    // 查询本组库位内有没有正在进行任务的库位
//    var occupyStorageNum = _Storage.AsQueryable()
//             .Where(x => x.RegionNum == "Region1710296466" && x.StorageOccupy == 2)
//             .OrderBy(x => x.StorageNum, OrderByType.Desc)
//             .ToList();

//    if (occupyStorageNum.Count != 0)
//    {
//        throw Oops.Oh("当前空框缓存库位里有正在进行中的任务，请等待任务执行完成后在次下发任务！");
//    }

//    // 查询出小料框缓存的库位
//    var data = _Storage.AsQueryable()
//                       .Where(x => x.RegionNum == "Region1710296466" && x.StorageOccupy == 0)
//                       .OrderBy(x => x.StorageNum, OrderByType.Desc)
//                       .ToList();

//    if (data.Count == 0)
//    {
//        throw Oops.Oh("当前小料框缓存库位已被全部暂用");
//    }

//    string storagenum = data.First().StorageNum.ToString();
//    return storagenum;

//}

#endregion
